From b6bacc384f9f451ddaecbd68ca634193b0b38de5 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Mon, 25 Feb 2019 12:29:05 +0200 Subject: [PATCH 01/70] comm: comment part added --- docx/document.py | 8 ++++ docx/opc/package.py | 14 ++++++ docx/oxml/__init__.py | 5 +++ docx/oxml/comments.py | 66 +++++++++++++++++++++++++++++ docx/parts/comments.py | 26 ++++++++++++ docx/parts/document.py | 8 ++++ docx/templates/default-comments.xml | 2 + 7 files changed, 129 insertions(+) create mode 100644 docx/oxml/comments.py create mode 100644 docx/parts/comments.py create mode 100644 docx/templates/default-comments.xml diff --git a/docx/document.py b/docx/document.py index ba94a7990..bf86c4f49 100644 --- a/docx/document.py +++ b/docx/document.py @@ -108,6 +108,14 @@ def core_properties(self): """ return self._part.core_properties + @property + def comments_part(self): + """ + A |Comments| object providing read/write access to the + comments of this document. + """ + return self._part.comments_part + @property def inline_shapes(self): """ diff --git a/docx/opc/package.py b/docx/opc/package.py index b0ea37ea5..5947c2b3a 100644 --- a/docx/opc/package.py +++ b/docx/opc/package.py @@ -11,6 +11,7 @@ from .packuri import PACKAGE_URI from .part import PartFactory from .parts.coreprops import CorePropertiesPart +from docx.parts.comments import CommentsPart from .pkgreader import PackageReader from .pkgwriter import PackageWriter from .rel import Relationships @@ -171,6 +172,19 @@ def _core_properties_part(self): core_properties_part = CorePropertiesPart.default(self) self.relate_to(core_properties_part, RT.CORE_PROPERTIES) return core_properties_part + + @property + def _comments_part(self): + """ + |CommentsPart| object related to this package. Creates + a default Comments part if one is not present. + """ + try: + return self.part_related_by(RT.COMMENTS) + except KeyError: + comments_part = CommentsPart.new(self) + self.relate_to(comments_part, RT.COMMENTS) + return comments_part class Unmarshaller(object): diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 2731302e2..31b2552f9 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -203,3 +203,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:br', CT_Br) register_element_cls('w:r', CT_R) register_element_cls('w:t', CT_Text) + + +from .comments import CT_Comments,CT_Com +register_element_cls('w:comments', CT_Comments) +register_element_cls('w:comment', CT_Com) \ No newline at end of file diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py new file mode 100644 index 000000000..dbf4b783b --- /dev/null +++ b/docx/oxml/comments.py @@ -0,0 +1,66 @@ +""" +Custom element classes related to the comments part +""" + +from . import OxmlElement +from .simpletypes import ST_DecimalNumber, ST_String +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne +) + +class CT_Com(BaseOxmlElement): + """ + A ```` element, a container for Comment properties + """ + initials = RequiredAttribute('w:initials', ST_String) + _id = RequiredAttribute('w:id', ST_DecimalNumber) + date = RequiredAttribute('w:date', ST_String) + author = RequiredAttribute('w:author', ST_String) + + paragraph = ZeroOrOne('w:p', successors=('w:comment',)) + + @classmethod + def new(cls, initials, comm_id, date, author): + """ + Return a new ```` element having _id of *comm_id* and having + the passed params as meta data + """ + comment = OxmlElement('w:comment') + comment.initials = initials + comment.date = date + comment._id = comm_id + comment.author = author + + return comment + def _add_p(self): + _p = OxmlElement('w:p') + self._insert_paragraph(_p) + return _p + +class CT_Comments(BaseOxmlElement): + """ + A ```` element, a container for Comments properties + """ + comment = ZeroOrMore ('w:comment', successors=('w:comments',)) + + def add_comment(self,author, initials, date): + _next_id = self._next_commentId + comment = CT_Com.new(initials, _next_id, date, author) + comment = self._insert_comment(comment) + + return comment + + @property + def _next_commentId(self): + ids = self.xpath('./w:comment/@w:id') + len(ids) + _ids = [int(_str) for _str in ids] + _ids.sort() + + print(_ids) + try: + return _ids[-1] + 2 + except: + return 0 + + diff --git a/docx/parts/comments.py b/docx/parts/comments.py new file mode 100644 index 000000000..6f41d776e --- /dev/null +++ b/docx/parts/comments.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +import os + +from docx.opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI + +from docx.oxml import parse_xml +from ..opc.part import XmlPart + +class CommentsPart(XmlPart): + """Definition of Comments Part""" + + @classmethod + def new(cls, package): + partname = PackURI("/word/comments.xml") + content_type = CT.WML_COMMENTS + element = parse_xml(cls._default_comments_xml()) + return cls(partname, content_type, element, package) + + @classmethod + def _default_comments_xml(cls): + path = os.path.join(os.path.split(__file__)[0], '..', 'templates', 'default-comments.xml') + with open(path, 'rb') as f: + xml_bytes = f.read() + return xml_bytes diff --git a/docx/parts/document.py b/docx/parts/document.py index 01266b3bd..59c38f9d0 100644 --- a/docx/parts/document.py +++ b/docx/parts/document.py @@ -121,6 +121,14 @@ def numbering_part(self): numbering_part = NumberingPart.new() self.relate_to(numbering_part, RT.NUMBERING) return numbering_part + + @lazyproperty + def comments_part(self): + """ + A |Comments| object providing read/write access to the + Comments of this document. + """ + return self.package._comments_part def save(self, path_or_stream): """ diff --git a/docx/templates/default-comments.xml b/docx/templates/default-comments.xml new file mode 100644 index 000000000..4ceb12ea4 --- /dev/null +++ b/docx/templates/default-comments.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From b66eaf720afb0662bb4ebc610019b89ec2974dea Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Tue, 26 Feb 2019 17:49:27 +0200 Subject: [PATCH 02/70] link paragraph to comment --- docx/__init__.py | 6 +++-- docx/document.py | 9 +++---- docx/opc/package.py | 2 +- docx/oxml/__init__.py | 7 ++++-- docx/oxml/comments.py | 48 +++++++++++++++++++++++++++++++++++-- docx/oxml/text/paragraph.py | 37 ++++++++++++++++++++++++++++ docx/oxml/text/run.py | 17 +++++++++++++ docx/parts/comments.py | 2 +- docx/parts/document.py | 25 +++++++++++++------ docx/text/paragraph.py | 20 ++++++++++++++++ 10 files changed, 154 insertions(+), 19 deletions(-) diff --git a/docx/__init__.py b/docx/__init__.py index 7083abe56..37205dc54 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -16,6 +16,7 @@ from docx.parts.numbering import NumberingPart from docx.parts.settings import SettingsPart from docx.parts.styles import StylesPart +from docx.parts.comments import CommentsPart def part_class_selector(content_type, reltype): @@ -25,6 +26,7 @@ def part_class_selector(content_type, reltype): PartFactory.part_class_selector = part_class_selector +PartFactory.part_type_for[CT.WML_COMMENTS] = CommentsPart PartFactory.part_type_for[CT.OPC_CORE_PROPERTIES] = CorePropertiesPart PartFactory.part_type_for[CT.WML_DOCUMENT_MAIN] = DocumentPart PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart @@ -32,6 +34,6 @@ def part_class_selector(content_type, reltype): PartFactory.part_type_for[CT.WML_STYLES] = StylesPart del ( - CT, CorePropertiesPart, DocumentPart, NumberingPart, PartFactory, - StylesPart, part_class_selector + CT, CorePropertiesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, + StylesPart, part_class_selector, ) diff --git a/docx/document.py b/docx/document.py index bf86c4f49..e87f3cc06 100644 --- a/docx/document.py +++ b/docx/document.py @@ -111,11 +111,12 @@ def core_properties(self): @property def comments_part(self): """ - A |Comments| object providing read/write access to the - comments of this document. + A |Comments| object providing read/write access to the core + properties of this document. """ - return self._part.comments_part - + return self._part._package._comments_part + + @property def inline_shapes(self): """ diff --git a/docx/opc/package.py b/docx/opc/package.py index 5947c2b3a..5b817274f 100644 --- a/docx/opc/package.py +++ b/docx/opc/package.py @@ -182,7 +182,7 @@ def _comments_part(self): try: return self.part_related_by(RT.COMMENTS) except KeyError: - comments_part = CommentsPart.new(self) + comments_part = CommentsPart.default(self) self.relate_to(comments_part, RT.COMMENTS) return comments_part diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 31b2552f9..fc6e18abc 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -205,6 +205,9 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:t', CT_Text) -from .comments import CT_Comments,CT_Com +from .comments import CT_Comments,CT_Com, CT_CRE, CT_CRS, CT_CRef register_element_cls('w:comments', CT_Comments) -register_element_cls('w:comment', CT_Com) \ No newline at end of file +register_element_cls('w:comment', CT_Com) +register_element_cls('w:commentRangeStart', CT_CRS) +register_element_cls('w:commentRangeEnd', CT_CRE) +register_element_cls('w:commentReference', CT_CRef) \ No newline at end of file diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index dbf4b783b..c4df76308 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -4,6 +4,7 @@ from . import OxmlElement from .simpletypes import ST_DecimalNumber, ST_String +from docx.text.run import Run from .xmlchemy import ( BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne ) @@ -30,10 +31,13 @@ def new(cls, initials, comm_id, date, author): comment.date = date comment._id = comm_id comment.author = author - return comment - def _add_p(self): + + def _add_p(self, text): _p = OxmlElement('w:p') + _r = _p.add_r() + run = Run(_r,self) + run.text = text self._insert_paragraph(_p) return _p @@ -64,3 +68,43 @@ def _next_commentId(self): return 0 +class CT_CRS(BaseOxmlElement): + """ + A ```` element + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + @classmethod + def new(cls, _id): + commentRangeStart = OxmlElement('w:commentRangeStart') + commentRangeStart._id =_id + + return commentRangeStart + + + +class CT_CRE(BaseOxmlElement): + """ + A ``w:commentRangeEnd`` element + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + + @classmethod + def new(cls, _id): + commentRangeEnd = OxmlElement('w:commentRangeEnd') + commentRangeEnd._id =_id + return commentRangeEnd + + +class CT_CRef(BaseOxmlElement): + """ + w:commentReference + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + @classmethod + def new (cls, _id): + commentReference = OxmlElement('w:commentReference') + commentReference._id =_id + return commentReference diff --git a/docx/oxml/text/paragraph.py b/docx/oxml/text/paragraph.py index 5e4213776..36bb40e81 100644 --- a/docx/oxml/text/paragraph.py +++ b/docx/oxml/text/paragraph.py @@ -26,6 +26,34 @@ def add_p_before(self): new_p = OxmlElement('w:p') self.addprevious(new_p) return new_p + + def link_comment(self, _id, rangeStart=0, rangeEnd=0): + rStart = OxmlElement('w:commentRangeStart') + rStart._id = _id + rEnd = OxmlElement('w:commentRangeEnd') + rEnd._id = _id + if rangeStart == 0 and rangeEnd == 0: + self.insert(0,rStart) + self.append(rEnd) + else: + self.insert(rangeStart,rStart) + if rangeEnd == len(self.getchildren() ) - 1 : + self.append(rEnd) + else: + self.insert(rangeEnd+1, rEnd) + + def add_comm(self, author, comment_part, initials, dtime, comment_text, rangeStart, rangeEnd): + + comment = comment_part.add_comment(author, initials, dtime) + comment._add_p(comment_text) + _r = self.add_r() + _r.add_comment_reference(comment._id) + self.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) + + return comment + + + @property def alignment(self): @@ -71,6 +99,15 @@ def style(self): if pPr is None: return None return pPr.style + + @property + def comment_id(self): + _id = self.xpath('./w:commentRangeStart/@w:id') + if(len(_id)>1): + return None + else: + return int(_id[0]) + @style.setter def style(self, style): diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index 8f0a62e82..010eb2935 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -6,10 +6,13 @@ from ..ns import qn from ..simpletypes import ST_BrClear, ST_BrType +from .. import OxmlElement from ..xmlchemy import ( BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne ) +from .. import OxmlElement + class CT_Br(BaseOxmlElement): """ @@ -24,6 +27,8 @@ class CT_R(BaseOxmlElement): ```` element, containing the properties and text for a run. """ rPr = ZeroOrOne('w:rPr') + ###wrong + ref = ZeroOrOne('w:commentRangeStart', successors=('w:r',)) t = ZeroOrMore('w:t') br = ZeroOrMore('w:br') cr = ZeroOrMore('w:cr') @@ -52,6 +57,12 @@ def add_drawing(self, inline_or_anchor): drawing.append(inline_or_anchor) return drawing + def add_comment_reference(self, _id): + reference = OxmlElement('w:commentReference') + reference._id = _id + self.append(reference) + return reference + def clear_content(self): """ Remove all child elements except the ```` element if present. @@ -60,6 +71,12 @@ def clear_content(self): for child in content_child_elms: self.remove(child) + def add_comment_reference(self, _id): + reference = OxmlElement('w:commentReference') + reference._id = _id + self.append(reference) + return reference + @property def style(self): """ diff --git a/docx/parts/comments.py b/docx/parts/comments.py index 6f41d776e..03a045aea 100644 --- a/docx/parts/comments.py +++ b/docx/parts/comments.py @@ -12,7 +12,7 @@ class CommentsPart(XmlPart): """Definition of Comments Part""" @classmethod - def new(cls, package): + def default(cls, package): partname = PackURI("/word/comments.xml") content_type = CT.WML_COMMENTS element = parse_xml(cls._default_comments_xml()) diff --git a/docx/parts/document.py b/docx/parts/document.py index 59c38f9d0..a10b1e690 100644 --- a/docx/parts/document.py +++ b/docx/parts/document.py @@ -17,6 +17,7 @@ from ..shared import lazyproperty from .settings import SettingsPart from .styles import StylesPart +from .comments import CommentsPart class DocumentPart(XmlPart): @@ -122,13 +123,7 @@ def numbering_part(self): self.relate_to(numbering_part, RT.NUMBERING) return numbering_part - @lazyproperty - def comments_part(self): - """ - A |Comments| object providing read/write access to the - Comments of this document. - """ - return self.package._comments_part + def save(self, path_or_stream): """ @@ -179,3 +174,19 @@ def _styles_part(self): styles_part = StylesPart.default(self.package) self.relate_to(styles_part, RT.STYLES) return styles_part + @lazyproperty + def comments_part(self): + """ + A |Comments| object providing read/write access to the core + properties of this document. + """ + # return self.package._comments_part + + @property + def _comments_part(self): + try: + return self.part_related_by(RT.COMMENTS) + except KeyError: + comments_part = CommentsPart.default(self) + self.relate_to(comments_part, RT.COMMENTS) + return comments_part \ No newline at end of file diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 4fb583b94..7c9a312ed 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -13,6 +13,7 @@ from .run import Run from ..shared import Parented +from datetime import datetime class Paragraph(Parented): """ @@ -39,6 +40,25 @@ def add_run(self, text=None, style=None): run.style = style return run + # def add_comment(self, author, initials, dt, comment_text, rangeStart=0, rangeEnd=0): + # comment_part = self.part.comments_part.element + # comment = comment_part.add_comment(author, initials, dt) + # comment._add_p(comment_text) + + # _r = self._p.add_r() + # _r.add_comment_reference(comment._id) + # self._p.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) + + # return comment + + def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0): + comment_part = self.part._comments_part.element + if dtime is None: + dtime = str( datetime.now ) + comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) + + return comment + @property def alignment(self): """ From 86686451c649cf4683011a9dc061897430f7a4e4 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 28 Feb 2019 09:19:53 +0200 Subject: [PATCH 03/70] no message --- build/lib/docx/__init__.py | 39 + build/lib/docx/api.py | 37 + build/lib/docx/blkcntnr.py | 74 ++ build/lib/docx/compat.py | 43 + build/lib/docx/dml/__init__.py | 0 build/lib/docx/dml/color.py | 116 +++ build/lib/docx/document.py | 224 +++++ build/lib/docx/enum/__init__.py | 18 + build/lib/docx/enum/base.py | 363 +++++++ build/lib/docx/enum/dml.py | 124 +++ build/lib/docx/enum/section.py | 76 ++ build/lib/docx/enum/shape.py | 21 + build/lib/docx/enum/style.py | 466 +++++++++ build/lib/docx/enum/table.py | 146 +++ build/lib/docx/enum/text.py | 351 +++++++ build/lib/docx/exceptions.py | 27 + build/lib/docx/image/__init__.py | 29 + build/lib/docx/image/bmp.py | 56 ++ build/lib/docx/image/constants.py | 169 ++++ build/lib/docx/image/exceptions.py | 23 + build/lib/docx/image/gif.py | 47 + build/lib/docx/image/helpers.py | 97 ++ build/lib/docx/image/image.py | 263 +++++ build/lib/docx/image/jpeg.py | 498 ++++++++++ build/lib/docx/image/png.py | 303 ++++++ build/lib/docx/image/tiff.py | 345 +++++++ build/lib/docx/opc/__init__.py | 0 build/lib/docx/opc/compat.py | 50 + build/lib/docx/opc/constants.py | 658 +++++++++++++ build/lib/docx/opc/coreprops.py | 139 +++ build/lib/docx/opc/exceptions.py | 19 + build/lib/docx/opc/oxml.py | 292 ++++++ build/lib/docx/opc/package.py | 235 +++++ build/lib/docx/opc/packuri.py | 117 +++ build/lib/docx/opc/part.py | 241 +++++ build/lib/docx/opc/parts/__init__.py | 0 build/lib/docx/opc/parts/coreprops.py | 54 ++ build/lib/docx/opc/phys_pkg.py | 155 +++ build/lib/docx/opc/pkgreader.py | 298 ++++++ build/lib/docx/opc/pkgwriter.py | 125 +++ build/lib/docx/opc/rel.py | 170 ++++ build/lib/docx/opc/shared.py | 47 + build/lib/docx/opc/spec.py | 29 + build/lib/docx/oxml/__init__.py | 213 +++++ build/lib/docx/oxml/comments.py | 110 +++ build/lib/docx/oxml/coreprops.py | 317 +++++++ build/lib/docx/oxml/document.py | 59 ++ build/lib/docx/oxml/exceptions.py | 16 + build/lib/docx/oxml/ns.py | 114 +++ build/lib/docx/oxml/numbering.py | 131 +++ build/lib/docx/oxml/section.py | 264 ++++++ build/lib/docx/oxml/shape.py | 284 ++++++ build/lib/docx/oxml/shared.py | 55 ++ build/lib/docx/oxml/simpletypes.py | 409 ++++++++ build/lib/docx/oxml/styles.py | 351 +++++++ build/lib/docx/oxml/table.py | 895 ++++++++++++++++++ build/lib/docx/oxml/text/__init__.py | 0 build/lib/docx/oxml/text/font.py | 320 +++++++ build/lib/docx/oxml/text/paragraph.py | 115 +++ build/lib/docx/oxml/text/parfmt.py | 348 +++++++ build/lib/docx/oxml/text/run.py | 183 ++++ build/lib/docx/oxml/xmlchemy.py | 761 +++++++++++++++ build/lib/docx/package.py | 115 +++ build/lib/docx/parts/__init__.py | 0 build/lib/docx/parts/comments.py | 26 + build/lib/docx/parts/document.py | 192 ++++ build/lib/docx/parts/image.py | 89 ++ build/lib/docx/parts/numbering.py | 47 + build/lib/docx/parts/settings.py | 54 ++ build/lib/docx/parts/styles.py | 55 ++ build/lib/docx/section.py | 185 ++++ build/lib/docx/settings.py | 20 + build/lib/docx/shape.py | 103 ++ build/lib/docx/shared.py | 250 +++++ build/lib/docx/styles/__init__.py | 50 + build/lib/docx/styles/latent.py | 224 +++++ build/lib/docx/styles/style.py | 265 ++++++ build/lib/docx/styles/styles.py | 157 +++ build/lib/docx/table.py | 469 +++++++++ build/lib/docx/templates/default-comments.xml | 2 + build/lib/docx/templates/default-settings.xml | 26 + build/lib/docx/templates/default-src.docx | Bin 0 -> 77650 bytes build/lib/docx/templates/default-styles.xml | 190 ++++ build/lib/docx/templates/default.docx | Bin 0 -> 38003 bytes build/lib/docx/text/__init__.py | 0 build/lib/docx/text/font.py | 411 ++++++++ build/lib/docx/text/paragraph.py | 165 ++++ build/lib/docx/text/parfmt.py | 303 ++++++ build/lib/docx/text/run.py | 191 ++++ build/lib/docx/text/tabstops.py | 143 +++ 90 files changed, 15261 insertions(+) create mode 100644 build/lib/docx/__init__.py create mode 100644 build/lib/docx/api.py create mode 100644 build/lib/docx/blkcntnr.py create mode 100644 build/lib/docx/compat.py create mode 100644 build/lib/docx/dml/__init__.py create mode 100644 build/lib/docx/dml/color.py create mode 100644 build/lib/docx/document.py create mode 100644 build/lib/docx/enum/__init__.py create mode 100644 build/lib/docx/enum/base.py create mode 100644 build/lib/docx/enum/dml.py create mode 100644 build/lib/docx/enum/section.py create mode 100644 build/lib/docx/enum/shape.py create mode 100644 build/lib/docx/enum/style.py create mode 100644 build/lib/docx/enum/table.py create mode 100644 build/lib/docx/enum/text.py create mode 100644 build/lib/docx/exceptions.py create mode 100644 build/lib/docx/image/__init__.py create mode 100644 build/lib/docx/image/bmp.py create mode 100644 build/lib/docx/image/constants.py create mode 100644 build/lib/docx/image/exceptions.py create mode 100644 build/lib/docx/image/gif.py create mode 100644 build/lib/docx/image/helpers.py create mode 100644 build/lib/docx/image/image.py create mode 100644 build/lib/docx/image/jpeg.py create mode 100644 build/lib/docx/image/png.py create mode 100644 build/lib/docx/image/tiff.py create mode 100644 build/lib/docx/opc/__init__.py create mode 100644 build/lib/docx/opc/compat.py create mode 100644 build/lib/docx/opc/constants.py create mode 100644 build/lib/docx/opc/coreprops.py create mode 100644 build/lib/docx/opc/exceptions.py create mode 100644 build/lib/docx/opc/oxml.py create mode 100644 build/lib/docx/opc/package.py create mode 100644 build/lib/docx/opc/packuri.py create mode 100644 build/lib/docx/opc/part.py create mode 100644 build/lib/docx/opc/parts/__init__.py create mode 100644 build/lib/docx/opc/parts/coreprops.py create mode 100644 build/lib/docx/opc/phys_pkg.py create mode 100644 build/lib/docx/opc/pkgreader.py create mode 100644 build/lib/docx/opc/pkgwriter.py create mode 100644 build/lib/docx/opc/rel.py create mode 100644 build/lib/docx/opc/shared.py create mode 100644 build/lib/docx/opc/spec.py create mode 100644 build/lib/docx/oxml/__init__.py create mode 100644 build/lib/docx/oxml/comments.py create mode 100644 build/lib/docx/oxml/coreprops.py create mode 100644 build/lib/docx/oxml/document.py create mode 100644 build/lib/docx/oxml/exceptions.py create mode 100644 build/lib/docx/oxml/ns.py create mode 100644 build/lib/docx/oxml/numbering.py create mode 100644 build/lib/docx/oxml/section.py create mode 100644 build/lib/docx/oxml/shape.py create mode 100644 build/lib/docx/oxml/shared.py create mode 100644 build/lib/docx/oxml/simpletypes.py create mode 100644 build/lib/docx/oxml/styles.py create mode 100644 build/lib/docx/oxml/table.py create mode 100644 build/lib/docx/oxml/text/__init__.py create mode 100644 build/lib/docx/oxml/text/font.py create mode 100644 build/lib/docx/oxml/text/paragraph.py create mode 100644 build/lib/docx/oxml/text/parfmt.py create mode 100644 build/lib/docx/oxml/text/run.py create mode 100644 build/lib/docx/oxml/xmlchemy.py create mode 100644 build/lib/docx/package.py create mode 100644 build/lib/docx/parts/__init__.py create mode 100644 build/lib/docx/parts/comments.py create mode 100644 build/lib/docx/parts/document.py create mode 100644 build/lib/docx/parts/image.py create mode 100644 build/lib/docx/parts/numbering.py create mode 100644 build/lib/docx/parts/settings.py create mode 100644 build/lib/docx/parts/styles.py create mode 100644 build/lib/docx/section.py create mode 100644 build/lib/docx/settings.py create mode 100644 build/lib/docx/shape.py create mode 100644 build/lib/docx/shared.py create mode 100644 build/lib/docx/styles/__init__.py create mode 100644 build/lib/docx/styles/latent.py create mode 100644 build/lib/docx/styles/style.py create mode 100644 build/lib/docx/styles/styles.py create mode 100644 build/lib/docx/table.py create mode 100644 build/lib/docx/templates/default-comments.xml create mode 100644 build/lib/docx/templates/default-settings.xml create mode 100644 build/lib/docx/templates/default-src.docx create mode 100644 build/lib/docx/templates/default-styles.xml create mode 100644 build/lib/docx/templates/default.docx create mode 100644 build/lib/docx/text/__init__.py create mode 100644 build/lib/docx/text/font.py create mode 100644 build/lib/docx/text/paragraph.py create mode 100644 build/lib/docx/text/parfmt.py create mode 100644 build/lib/docx/text/run.py create mode 100644 build/lib/docx/text/tabstops.py diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py new file mode 100644 index 000000000..37205dc54 --- /dev/null +++ b/build/lib/docx/__init__.py @@ -0,0 +1,39 @@ +# encoding: utf-8 + +from docx.api import Document # noqa + +__version__ = '0.8.7' + + +# register custom Part classes with opc package reader + +from docx.opc.constants import CONTENT_TYPE as CT, RELATIONSHIP_TYPE as RT +from docx.opc.part import PartFactory +from docx.opc.parts.coreprops import CorePropertiesPart + +from docx.parts.document import DocumentPart +from docx.parts.image import ImagePart +from docx.parts.numbering import NumberingPart +from docx.parts.settings import SettingsPart +from docx.parts.styles import StylesPart +from docx.parts.comments import CommentsPart + + +def part_class_selector(content_type, reltype): + if reltype == RT.IMAGE: + return ImagePart + return None + + +PartFactory.part_class_selector = part_class_selector +PartFactory.part_type_for[CT.WML_COMMENTS] = CommentsPart +PartFactory.part_type_for[CT.OPC_CORE_PROPERTIES] = CorePropertiesPart +PartFactory.part_type_for[CT.WML_DOCUMENT_MAIN] = DocumentPart +PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart +PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart +PartFactory.part_type_for[CT.WML_STYLES] = StylesPart + +del ( + CT, CorePropertiesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, + StylesPart, part_class_selector, +) diff --git a/build/lib/docx/api.py b/build/lib/docx/api.py new file mode 100644 index 000000000..63e18c406 --- /dev/null +++ b/build/lib/docx/api.py @@ -0,0 +1,37 @@ +# encoding: utf-8 + +""" +Directly exposed API functions and classes, :func:`Document` for now. +Provides a syntactically more convenient API for interacting with the +OpcPackage graph. +""" + +from __future__ import absolute_import, division, print_function + +import os + +from docx.opc.constants import CONTENT_TYPE as CT +from docx.package import Package + + +def Document(docx=None): + """ + Return a |Document| object loaded from *docx*, where *docx* can be + either a path to a ``.docx`` file (a string) or a file-like object. If + *docx* is missing or ``None``, the built-in default document "template" + is loaded. + """ + docx = _default_docx_path() if docx is None else docx + document_part = Package.open(docx).main_document_part + if document_part.content_type != CT.WML_DOCUMENT_MAIN: + tmpl = "file '%s' is not a Word file, content type is '%s'" + raise ValueError(tmpl % (docx, document_part.content_type)) + return document_part.document + + +def _default_docx_path(): + """ + Return the path to the built-in default .docx package. + """ + _thisdir = os.path.split(__file__)[0] + return os.path.join(_thisdir, 'templates', 'default.docx') diff --git a/build/lib/docx/blkcntnr.py b/build/lib/docx/blkcntnr.py new file mode 100644 index 000000000..d57a0cd0f --- /dev/null +++ b/build/lib/docx/blkcntnr.py @@ -0,0 +1,74 @@ +# encoding: utf-8 + +""" +Block item container, used by body, cell, header, etc. Block level items are +things like paragraph and table, although there are a few other specialized +ones like structured document tags. +""" + +from __future__ import absolute_import, print_function + +from .oxml.table import CT_Tbl +from .shared import Parented +from .text.paragraph import Paragraph + + +class BlockItemContainer(Parented): + """ + Base class for proxy objects that can contain block items, such as _Body, + _Cell, header, footer, footnote, endnote, comment, and text box objects. + Provides the shared functionality to add a block item like a paragraph or + table. + """ + def __init__(self, element, parent): + super(BlockItemContainer, self).__init__(parent) + self._element = element + + def add_paragraph(self, text='', style=None): + """ + Return a paragraph newly added to the end of the content in this + container, having *text* in a single run if present, and having + paragraph style *style*. If *style* is |None|, no paragraph style is + applied, which has the same effect as applying the 'Normal' style. + """ + paragraph = self._add_paragraph() + if text: + paragraph.add_run(text) + if style is not None: + paragraph.style = style + return paragraph + + def add_table(self, rows, cols, width): + """ + Return a table of *width* having *rows* rows and *cols* columns, + newly appended to the content in this container. *width* is evenly + distributed between the table columns. + """ + from .table import Table + tbl = CT_Tbl.new_tbl(rows, cols, width) + self._element._insert_tbl(tbl) + return Table(tbl, self) + + @property + def paragraphs(self): + """ + A list containing the paragraphs in this container, in document + order. Read-only. + """ + return [Paragraph(p, self) for p in self._element.p_lst] + + @property + def tables(self): + """ + A list containing the tables in this container, in document order. + Read-only. + """ + from .table import Table + return [Table(tbl, self) for tbl in self._element.tbl_lst] + + def _add_paragraph(self): + """ + Return a paragraph newly added to the end of the content in this + container. + """ + return Paragraph(self._element.add_p(), self) diff --git a/build/lib/docx/compat.py b/build/lib/docx/compat.py new file mode 100644 index 000000000..dc9e20e39 --- /dev/null +++ b/build/lib/docx/compat.py @@ -0,0 +1,43 @@ +# encoding: utf-8 + +""" +Provides Python 2/3 compatibility objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import sys + +# =========================================================================== +# Python 3 versions +# =========================================================================== + +if sys.version_info >= (3, 0): + + from io import BytesIO + + def is_string(obj): + """ + Return True if *obj* is a string, False otherwise. + """ + return isinstance(obj, str) + + Unicode = str + +# =========================================================================== +# Python 2 versions +# =========================================================================== + +else: + + from StringIO import StringIO as BytesIO # noqa + + def is_string(obj): + """ + Return True if *obj* is a string, False otherwise. + """ + return isinstance(obj, basestring) + + Unicode = unicode diff --git a/build/lib/docx/dml/__init__.py b/build/lib/docx/dml/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/lib/docx/dml/color.py b/build/lib/docx/dml/color.py new file mode 100644 index 000000000..2f2f25cb2 --- /dev/null +++ b/build/lib/docx/dml/color.py @@ -0,0 +1,116 @@ +# encoding: utf-8 + +""" +DrawingML objects related to color, ColorFormat being the most prominent. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..enum.dml import MSO_COLOR_TYPE +from ..oxml.simpletypes import ST_HexColorAuto +from ..shared import ElementProxy + + +class ColorFormat(ElementProxy): + """ + Provides access to color settings such as RGB color, theme color, and + luminance adjustments. + """ + + __slots__ = () + + def __init__(self, rPr_parent): + super(ColorFormat, self).__init__(rPr_parent) + + @property + def rgb(self): + """ + An |RGBColor| value or |None| if no RGB color is specified. + + When :attr:`type` is `MSO_COLOR_TYPE.RGB`, the value of this property + will always be an |RGBColor| value. It may also be an |RGBColor| + value if :attr:`type` is `MSO_COLOR_TYPE.THEME`, as Word writes the + current value of a theme color when one is assigned. In that case, + the RGB value should be interpreted as no more than a good guess + however, as the theme color takes precedence at rendering time. Its + value is |None| whenever :attr:`type` is either |None| or + `MSO_COLOR_TYPE.AUTO`. + + Assigning an |RGBColor| value causes :attr:`type` to become + `MSO_COLOR_TYPE.RGB` and any theme color is removed. Assigning |None| + causes any color to be removed such that the effective color is + inherited from the style hierarchy. + """ + color = self._color + if color is None: + return None + if color.val == ST_HexColorAuto.AUTO: + return None + return color.val + + @rgb.setter + def rgb(self, value): + if value is None and self._color is None: + return + rPr = self._element.get_or_add_rPr() + rPr._remove_color() + if value is not None: + rPr.get_or_add_color().val = value + + @property + def theme_color(self): + """ + A member of :ref:`MsoThemeColorIndex` or |None| if no theme color is + specified. When :attr:`type` is `MSO_COLOR_TYPE.THEME`, the value of + this property will always be a member of :ref:`MsoThemeColorIndex`. + When :attr:`type` has any other value, the value of this property is + |None|. + + Assigning a member of :ref:`MsoThemeColorIndex` causes :attr:`type` + to become `MSO_COLOR_TYPE.THEME`. Any existing RGB value is retained + but ignored by Word. Assigning |None| causes any color specification + to be removed such that the effective color is inherited from the + style hierarchy. + """ + color = self._color + if color is None or color.themeColor is None: + return None + return color.themeColor + + @theme_color.setter + def theme_color(self, value): + if value is None: + if self._color is not None: + self._element.rPr._remove_color() + return + self._element.get_or_add_rPr().get_or_add_color().themeColor = value + + @property + def type(self): + """ + Read-only. A member of :ref:`MsoColorType`, one of RGB, THEME, or + AUTO, corresponding to the way this color is defined. Its value is + |None| if no color is applied at this level, which causes the + effective color to be inherited from the style hierarchy. + """ + color = self._color + if color is None: + return None + if color.themeColor is not None: + return MSO_COLOR_TYPE.THEME + if color.val == ST_HexColorAuto.AUTO: + return MSO_COLOR_TYPE.AUTO + return MSO_COLOR_TYPE.RGB + + @property + def _color(self): + """ + Return `w:rPr/w:color` or |None| if not present. Helper to factor out + repetitive element access. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.color diff --git a/build/lib/docx/document.py b/build/lib/docx/document.py new file mode 100644 index 000000000..e87f3cc06 --- /dev/null +++ b/build/lib/docx/document.py @@ -0,0 +1,224 @@ +# encoding: utf-8 + +""" +|Document| and closely related objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .blkcntnr import BlockItemContainer +from .enum.section import WD_SECTION +from .enum.text import WD_BREAK +from .section import Section, Sections +from .shared import ElementProxy, Emu + + +class Document(ElementProxy): + """ + WordprocessingML (WML) document. Not intended to be constructed directly. + Use :func:`docx.Document` to open or create a document. + """ + + __slots__ = ('_part', '__body') + + def __init__(self, element, part): + super(Document, self).__init__(element) + self._part = part + self.__body = None + + def add_heading(self, text='', level=1): + """ + Return a heading paragraph newly added to the end of the document, + containing *text* and having its paragraph style determined by + *level*. If *level* is 0, the style is set to `Title`. If *level* is + 1 (or omitted), `Heading 1` is used. Otherwise the style is set to + `Heading {level}`. Raises |ValueError| if *level* is outside the + range 0-9. + """ + if not 0 <= level <= 9: + raise ValueError("level must be in range 0-9, got %d" % level) + style = 'Title' if level == 0 else 'Heading %d' % level + return self.add_paragraph(text, style) + + def add_page_break(self): + """ + Return a paragraph newly added to the end of the document and + containing only a page break. + """ + paragraph = self.add_paragraph() + paragraph.add_run().add_break(WD_BREAK.PAGE) + return paragraph + + def add_paragraph(self, text='', style=None): + """ + Return a paragraph newly added to the end of the document, populated + with *text* and having paragraph style *style*. *text* can contain + tab (``\\t``) characters, which are converted to the appropriate XML + form for a tab. *text* can also include newline (``\\n``) or carriage + return (``\\r``) characters, each of which is converted to a line + break. + """ + return self._body.add_paragraph(text, style) + + def add_picture(self, image_path_or_stream, width=None, height=None): + """ + Return a new picture shape added in its own paragraph at the end of + the document. The picture contains the image at + *image_path_or_stream*, scaled based on *width* and *height*. If + neither width nor height is specified, the picture appears at its + native size. If only one is specified, it is used to compute + a scaling factor that is then applied to the unspecified dimension, + preserving the aspect ratio of the image. The native size of the + picture is calculated using the dots-per-inch (dpi) value specified + in the image file, defaulting to 72 dpi if no value is specified, as + is often the case. + """ + run = self.add_paragraph().add_run() + return run.add_picture(image_path_or_stream, width, height) + + def add_section(self, start_type=WD_SECTION.NEW_PAGE): + """ + Return a |Section| object representing a new section added at the end + of the document. The optional *start_type* argument must be a member + of the :ref:`WdSectionStart` enumeration, and defaults to + ``WD_SECTION.NEW_PAGE`` if not provided. + """ + new_sectPr = self._element.body.add_section_break() + new_sectPr.start_type = start_type + return Section(new_sectPr) + + def add_table(self, rows, cols, style=None): + """ + Add a table having row and column counts of *rows* and *cols* + respectively and table style of *style*. *style* may be a paragraph + style object or a paragraph style name. If *style* is |None|, the + table inherits the default table style of the document. + """ + table = self._body.add_table(rows, cols, self._block_width) + table.style = style + return table + + @property + def core_properties(self): + """ + A |CoreProperties| object providing read/write access to the core + properties of this document. + """ + return self._part.core_properties + + @property + def comments_part(self): + """ + A |Comments| object providing read/write access to the core + properties of this document. + """ + return self._part._package._comments_part + + + @property + def inline_shapes(self): + """ + An |InlineShapes| object providing access to the inline shapes in + this document. An inline shape is a graphical object, such as + a picture, contained in a run of text and behaving like a character + glyph, being flowed like other text in a paragraph. + """ + return self._part.inline_shapes + + @property + def paragraphs(self): + """ + A list of |Paragraph| instances corresponding to the paragraphs in + the document, in document order. Note that paragraphs within revision + marks such as ```` or ```` do not appear in this list. + """ + return self._body.paragraphs + + @property + def part(self): + """ + The |DocumentPart| object of this document. + """ + return self._part + + def save(self, path_or_stream): + """ + Save this document to *path_or_stream*, which can be either a path to + a filesystem location (a string) or a file-like object. + """ + self._part.save(path_or_stream) + + @property + def sections(self): + """ + A |Sections| object providing access to each section in this + document. + """ + return Sections(self._element) + + @property + def settings(self): + """ + A |Settings| object providing access to the document-level settings + for this document. + """ + return self._part.settings + + @property + def styles(self): + """ + A |Styles| object providing access to the styles in this document. + """ + return self._part.styles + + @property + def tables(self): + """ + A list of |Table| instances corresponding to the tables in the + document, in document order. Note that only tables appearing at the + top level of the document appear in this list; a table nested inside + a table cell does not appear. A table within revision marks such as + ```` or ```` will also not appear in the list. + """ + return self._body.tables + + @property + def _block_width(self): + """ + Return a |Length| object specifying the width of available "writing" + space between the margins of the last section of this document. + """ + section = self.sections[-1] + return Emu( + section.page_width - section.left_margin - section.right_margin + ) + + @property + def _body(self): + """ + The |_Body| instance containing the content for this document. + """ + if self.__body is None: + self.__body = _Body(self._element.body, self) + return self.__body + + +class _Body(BlockItemContainer): + """ + Proxy for ```` element in this document, having primarily a + container role. + """ + def __init__(self, body_elm, parent): + super(_Body, self).__init__(body_elm, parent) + self._body = body_elm + + def clear_content(self): + """ + Return this |_Body| instance after clearing it of all content. + Section properties for the main document story, if present, are + preserved. + """ + self._body.clear_content() + return self diff --git a/build/lib/docx/enum/__init__.py b/build/lib/docx/enum/__init__.py new file mode 100644 index 000000000..dd49faafd --- /dev/null +++ b/build/lib/docx/enum/__init__.py @@ -0,0 +1,18 @@ +# encoding: utf-8 + +""" +Enumerations used in python-docx +""" + +from __future__ import absolute_import, print_function, unicode_literals + + +class Enumeration(object): + + @classmethod + def from_xml(cls, xml_val): + return cls._xml_to_idx[xml_val] + + @classmethod + def to_xml(cls, enum_val): + return cls._idx_to_xml[enum_val] diff --git a/build/lib/docx/enum/base.py b/build/lib/docx/enum/base.py new file mode 100644 index 000000000..36764b1a6 --- /dev/null +++ b/build/lib/docx/enum/base.py @@ -0,0 +1,363 @@ +# encoding: utf-8 + +""" +Base classes and other objects used by enumerations +""" + +from __future__ import absolute_import, print_function + +import sys +import textwrap + +from ..exceptions import InvalidXmlError + + +def alias(*aliases): + """ + Decorating a class with @alias('FOO', 'BAR', ..) allows the class to + be referenced by each of the names provided as arguments. + """ + def decorator(cls): + # alias must be set in globals from caller's frame + caller = sys._getframe(1) + globals_dict = caller.f_globals + for alias in aliases: + globals_dict[alias] = cls + return cls + return decorator + + +class _DocsPageFormatter(object): + """Generate an .rst doc page for an enumeration. + + Formats a RestructuredText documention page (string) for the enumeration + class parts passed to the constructor. An immutable one-shot service + object. + """ + + def __init__(self, clsname, clsdict): + self._clsname = clsname + self._clsdict = clsdict + + @property + def page_str(self): + """ + The RestructuredText documentation page for the enumeration. This is + the only API member for the class. + """ + tmpl = '.. _%s:\n\n%s\n\n%s\n\n----\n\n%s' + components = ( + self._ms_name, self._page_title, self._intro_text, + self._member_defs + ) + return tmpl % components + + @property + def _intro_text(self): + """Docstring of the enumeration, formatted for documentation page.""" + try: + cls_docstring = self._clsdict['__doc__'] + except KeyError: + cls_docstring = '' + + if cls_docstring is None: + return '' + + return textwrap.dedent(cls_docstring).strip() + + def _member_def(self, member): + """ + Return an individual member definition formatted as an RST glossary + entry, wrapped to fit within 78 columns. + """ + member_docstring = textwrap.dedent(member.docstring).strip() + member_docstring = textwrap.fill( + member_docstring, width=78, initial_indent=' '*4, + subsequent_indent=' '*4 + ) + return '%s\n%s\n' % (member.name, member_docstring) + + @property + def _member_defs(self): + """ + A single string containing the aggregated member definitions section + of the documentation page + """ + members = self._clsdict['__members__'] + member_defs = [ + self._member_def(member) for member in members + if member.name is not None + ] + return '\n'.join(member_defs) + + @property + def _ms_name(self): + """ + The Microsoft API name for this enumeration + """ + return self._clsdict['__ms_name__'] + + @property + def _page_title(self): + """ + The title for the documentation page, formatted as code (surrounded + in double-backtics) and underlined with '=' characters + """ + title_underscore = '=' * (len(self._clsname)+4) + return '``%s``\n%s' % (self._clsname, title_underscore) + + +class MetaEnumeration(type): + """ + The metaclass for Enumeration and its subclasses. Adds a name for each + named member and compiles state needed by the enumeration class to + respond to other attribute gets + """ + def __new__(meta, clsname, bases, clsdict): + meta._add_enum_members(clsdict) + meta._collect_valid_settings(clsdict) + meta._generate_docs_page(clsname, clsdict) + return type.__new__(meta, clsname, bases, clsdict) + + @classmethod + def _add_enum_members(meta, clsdict): + """ + Dispatch ``.add_to_enum()`` call to each member so it can do its + thing to properly add itself to the enumeration class. This + delegation allows member sub-classes to add specialized behaviors. + """ + enum_members = clsdict['__members__'] + for member in enum_members: + member.add_to_enum(clsdict) + + @classmethod + def _collect_valid_settings(meta, clsdict): + """ + Return a sequence containing the enumeration values that are valid + assignment values. Return-only values are excluded. + """ + enum_members = clsdict['__members__'] + valid_settings = [] + for member in enum_members: + valid_settings.extend(member.valid_settings) + clsdict['_valid_settings'] = valid_settings + + @classmethod + def _generate_docs_page(meta, clsname, clsdict): + """ + Return the RST documentation page for the enumeration. + """ + clsdict['__docs_rst__'] = ( + _DocsPageFormatter(clsname, clsdict).page_str + ) + + +class EnumerationBase(object): + """ + Base class for all enumerations, used directly for enumerations requiring + only basic behavior. It's __dict__ is used below in the Python 2+3 + compatible metaclass definition. + """ + __members__ = () + __ms_name__ = '' + + @classmethod + def validate(cls, value): + """ + Raise |ValueError| if *value* is not an assignable value. + """ + if value not in cls._valid_settings: + raise ValueError( + "%s not a member of %s enumeration" % (value, cls.__name__) + ) + + +Enumeration = MetaEnumeration( + 'Enumeration', (object,), dict(EnumerationBase.__dict__) +) + + +class XmlEnumeration(Enumeration): + """ + Provides ``to_xml()`` and ``from_xml()`` methods in addition to base + enumeration features + """ + __members__ = () + __ms_name__ = '' + + @classmethod + def from_xml(cls, xml_val): + """ + Return the enumeration member corresponding to the XML value + *xml_val*. + """ + if xml_val not in cls._xml_to_member: + raise InvalidXmlError( + "attribute value '%s' not valid for this type" % xml_val + ) + return cls._xml_to_member[xml_val] + + @classmethod + def to_xml(cls, enum_val): + """ + Return the XML value of the enumeration value *enum_val*. + """ + if enum_val not in cls._member_to_xml: + raise ValueError( + "value '%s' not in enumeration %s" % (enum_val, cls.__name__) + ) + return cls._member_to_xml[enum_val] + + +class EnumMember(object): + """ + Used in the enumeration class definition to define a member value and its + mappings + """ + def __init__(self, name, value, docstring): + self._name = name + if isinstance(value, int): + value = EnumValue(name, value, docstring) + self._value = value + self._docstring = docstring + + def add_to_enum(self, clsdict): + """ + Add a name to *clsdict* for this member. + """ + self.register_name(clsdict) + + @property + def docstring(self): + """ + The description of this member + """ + return self._docstring + + @property + def name(self): + """ + The distinguishing name of this member within the enumeration class, + e.g. 'MIDDLE' for MSO_VERTICAL_ANCHOR.MIDDLE, if this is a named + member. Otherwise the primitive value such as |None|, |True| or + |False|. + """ + return self._name + + def register_name(self, clsdict): + """ + Add a member name to the class dict *clsdict* containing the value of + this member object. Where the name of this object is None, do + nothing; this allows out-of-band values to be defined without adding + a name to the class dict. + """ + if self.name is None: + return + clsdict[self.name] = self.value + + @property + def valid_settings(self): + """ + A sequence containing the values valid for assignment for this + member. May be zero, one, or more in number. + """ + return (self._value,) + + @property + def value(self): + """ + The enumeration value for this member, often an instance of + EnumValue, but may be a primitive value such as |None|. + """ + return self._value + + +class EnumValue(int): + """ + A named enumeration value, providing __str__ and __doc__ string values + for its symbolic name and description, respectively. Subclasses int, so + behaves as a regular int unless the strings are asked for. + """ + def __new__(cls, member_name, int_value, docstring): + return super(EnumValue, cls).__new__(cls, int_value) + + def __init__(self, member_name, int_value, docstring): + super(EnumValue, self).__init__() + self._member_name = member_name + self._docstring = docstring + + @property + def __doc__(self): + """ + The description of this enumeration member + """ + return self._docstring.strip() + + def __str__(self): + """ + The symbolic name and string value of this member, e.g. 'MIDDLE (3)' + """ + return "%s (%d)" % (self._member_name, int(self)) + + +class ReturnValueOnlyEnumMember(EnumMember): + """ + Used to define a member of an enumeration that is only valid as a query + result and is not valid as a setting, e.g. MSO_VERTICAL_ANCHOR.MIXED (-2) + """ + @property + def valid_settings(self): + """ + No settings are valid for a return-only value. + """ + return () + + +class XmlMappedEnumMember(EnumMember): + """ + Used to define a member whose value maps to an XML attribute value. + """ + def __init__(self, name, value, xml_value, docstring): + super(XmlMappedEnumMember, self).__init__(name, value, docstring) + self._xml_value = xml_value + + def add_to_enum(self, clsdict): + """ + Compile XML mappings in addition to base add behavior. + """ + super(XmlMappedEnumMember, self).add_to_enum(clsdict) + self.register_xml_mapping(clsdict) + + def register_xml_mapping(self, clsdict): + """ + Add XML mappings to the enumeration class state for this member. + """ + member_to_xml = self._get_or_add_member_to_xml(clsdict) + member_to_xml[self.value] = self.xml_value + xml_to_member = self._get_or_add_xml_to_member(clsdict) + xml_to_member[self.xml_value] = self.value + + @property + def xml_value(self): + """ + The XML attribute value that corresponds to this enumeration value + """ + return self._xml_value + + @staticmethod + def _get_or_add_member_to_xml(clsdict): + """ + Add the enum -> xml value mapping to the enumeration class state + """ + if '_member_to_xml' not in clsdict: + clsdict['_member_to_xml'] = dict() + return clsdict['_member_to_xml'] + + @staticmethod + def _get_or_add_xml_to_member(clsdict): + """ + Add the xml -> enum value mapping to the enumeration class state + """ + if '_xml_to_member' not in clsdict: + clsdict['_xml_to_member'] = dict() + return clsdict['_xml_to_member'] diff --git a/build/lib/docx/enum/dml.py b/build/lib/docx/enum/dml.py new file mode 100644 index 000000000..1ad0eaa87 --- /dev/null +++ b/build/lib/docx/enum/dml.py @@ -0,0 +1,124 @@ +# encoding: utf-8 + +""" +Enumerations used by DrawingML objects +""" + +from __future__ import absolute_import + +from .base import ( + alias, Enumeration, EnumMember, XmlEnumeration, XmlMappedEnumMember +) + + +class MSO_COLOR_TYPE(Enumeration): + """ + Specifies the color specification scheme + + Example:: + + from docx.enum.dml import MSO_COLOR_TYPE + + assert font.color.type == MSO_COLOR_TYPE.SCHEME + """ + + __ms_name__ = 'MsoColorType' + + __url__ = ( + 'http://msdn.microsoft.com/en-us/library/office/ff864912(v=office.15' + ').aspx' + ) + + __members__ = ( + EnumMember( + 'RGB', 1, 'Color is specified by an |RGBColor| value.' + ), + EnumMember( + 'THEME', 2, 'Color is one of the preset theme colors.' + ), + EnumMember( + 'AUTO', 101, 'Color is determined automatically by the ' + 'application.' + ), + ) + + +@alias('MSO_THEME_COLOR') +class MSO_THEME_COLOR_INDEX(XmlEnumeration): + """ + Indicates the Office theme color, one of those shown in the color gallery + on the formatting ribbon. + + Alias: ``MSO_THEME_COLOR`` + + Example:: + + from docx.enum.dml import MSO_THEME_COLOR + + font.color.theme_color = MSO_THEME_COLOR.ACCENT_1 + """ + + __ms_name__ = 'MsoThemeColorIndex' + + __url__ = ( + 'http://msdn.microsoft.com/en-us/library/office/ff860782(v=office.15' + ').aspx' + ) + + __members__ = ( + EnumMember( + 'NOT_THEME_COLOR', 0, 'Indicates the color is not a theme color.' + ), + XmlMappedEnumMember( + 'ACCENT_1', 5, 'accent1', 'Specifies the Accent 1 theme color.' + ), + XmlMappedEnumMember( + 'ACCENT_2', 6, 'accent2', 'Specifies the Accent 2 theme color.' + ), + XmlMappedEnumMember( + 'ACCENT_3', 7, 'accent3', 'Specifies the Accent 3 theme color.' + ), + XmlMappedEnumMember( + 'ACCENT_4', 8, 'accent4', 'Specifies the Accent 4 theme color.' + ), + XmlMappedEnumMember( + 'ACCENT_5', 9, 'accent5', 'Specifies the Accent 5 theme color.' + ), + XmlMappedEnumMember( + 'ACCENT_6', 10, 'accent6', 'Specifies the Accent 6 theme color.' + ), + XmlMappedEnumMember( + 'BACKGROUND_1', 14, 'background1', 'Specifies the Background 1 ' + 'theme color.' + ), + XmlMappedEnumMember( + 'BACKGROUND_2', 16, 'background2', 'Specifies the Background 2 ' + 'theme color.' + ), + XmlMappedEnumMember( + 'DARK_1', 1, 'dark1', 'Specifies the Dark 1 theme color.' + ), + XmlMappedEnumMember( + 'DARK_2', 3, 'dark2', 'Specifies the Dark 2 theme color.' + ), + XmlMappedEnumMember( + 'FOLLOWED_HYPERLINK', 12, 'followedHyperlink', 'Specifies the ' + 'theme color for a clicked hyperlink.' + ), + XmlMappedEnumMember( + 'HYPERLINK', 11, 'hyperlink', 'Specifies the theme color for a ' + 'hyperlink.' + ), + XmlMappedEnumMember( + 'LIGHT_1', 2, 'light1', 'Specifies the Light 1 theme color.' + ), + XmlMappedEnumMember( + 'LIGHT_2', 4, 'light2', 'Specifies the Light 2 theme color.' + ), + XmlMappedEnumMember( + 'TEXT_1', 13, 'text1', 'Specifies the Text 1 theme color.' + ), + XmlMappedEnumMember( + 'TEXT_2', 15, 'text2', 'Specifies the Text 2 theme color.' + ), + ) diff --git a/build/lib/docx/enum/section.py b/build/lib/docx/enum/section.py new file mode 100644 index 000000000..b16ddbe72 --- /dev/null +++ b/build/lib/docx/enum/section.py @@ -0,0 +1,76 @@ +# encoding: utf-8 + +""" +Enumerations related to the main document in WordprocessingML files +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from .base import alias, XmlEnumeration, XmlMappedEnumMember + + +@alias('WD_ORIENT') +class WD_ORIENTATION(XmlEnumeration): + """ + alias: **WD_ORIENT** + + Specifies the page layout orientation. + + Example:: + + from docx.enum.section import WD_ORIENT + + section = document.sections[-1] + section.orientation = WD_ORIENT.LANDSCAPE + """ + + __ms_name__ = 'WdOrientation' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff837902.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'PORTRAIT', 0, 'portrait', 'Portrait orientation.' + ), + XmlMappedEnumMember( + 'LANDSCAPE', 1, 'landscape', 'Landscape orientation.' + ), + ) + + +@alias('WD_SECTION') +class WD_SECTION_START(XmlEnumeration): + """ + alias: **WD_SECTION** + + Specifies the start type of a section break. + + Example:: + + from docx.enum.section import WD_SECTION + + section = document.sections[0] + section.start_type = WD_SECTION.NEW_PAGE + """ + + __ms_name__ = 'WdSectionStart' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff840975.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'CONTINUOUS', 0, 'continuous', 'Continuous section break.' + ), + XmlMappedEnumMember( + 'NEW_COLUMN', 1, 'nextColumn', 'New column section break.' + ), + XmlMappedEnumMember( + 'NEW_PAGE', 2, 'nextPage', 'New page section break.' + ), + XmlMappedEnumMember( + 'EVEN_PAGE', 3, 'evenPage', 'Even pages section break.' + ), + XmlMappedEnumMember( + 'ODD_PAGE', 4, 'oddPage', 'Section begins on next odd page.' + ), + ) diff --git a/build/lib/docx/enum/shape.py b/build/lib/docx/enum/shape.py new file mode 100644 index 000000000..f1d6ffd8c --- /dev/null +++ b/build/lib/docx/enum/shape.py @@ -0,0 +1,21 @@ +# encoding: utf-8 + +""" +Enumerations related to DrawingML shapes in WordprocessingML files +""" + +from __future__ import absolute_import, print_function, unicode_literals + + +class WD_INLINE_SHAPE_TYPE(object): + """ + Corresponds to WdInlineShapeType enumeration + http://msdn.microsoft.com/en-us/library/office/ff192587.aspx + """ + CHART = 12 + LINKED_PICTURE = 4 + PICTURE = 3 + SMART_ART = 15 + NOT_IMPLEMENTED = -6 + +WD_INLINE_SHAPE = WD_INLINE_SHAPE_TYPE diff --git a/build/lib/docx/enum/style.py b/build/lib/docx/enum/style.py new file mode 100644 index 000000000..515c594ce --- /dev/null +++ b/build/lib/docx/enum/style.py @@ -0,0 +1,466 @@ +# encoding: utf-8 + +""" +Enumerations related to styles +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from .base import alias, EnumMember, XmlEnumeration, XmlMappedEnumMember + + +@alias('WD_STYLE') +class WD_BUILTIN_STYLE(XmlEnumeration): + """ + alias: **WD_STYLE** + + Specifies a built-in Microsoft Word style. + + Example:: + + from docx import Document + from docx.enum.style import WD_STYLE + + document = Document() + styles = document.styles + style = styles[WD_STYLE.BODY_TEXT] + """ + + __ms_name__ = 'WdBuiltinStyle' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff835210.aspx' + + __members__ = ( + EnumMember( + 'BLOCK_QUOTATION', -85, 'Block Text.' + ), + EnumMember( + 'BODY_TEXT', -67, 'Body Text.' + ), + EnumMember( + 'BODY_TEXT_2', -81, 'Body Text 2.' + ), + EnumMember( + 'BODY_TEXT_3', -82, 'Body Text 3.' + ), + EnumMember( + 'BODY_TEXT_FIRST_INDENT', -78, 'Body Text First Indent.' + ), + EnumMember( + 'BODY_TEXT_FIRST_INDENT_2', -79, 'Body Text First Indent 2.' + ), + EnumMember( + 'BODY_TEXT_INDENT', -68, 'Body Text Indent.' + ), + EnumMember( + 'BODY_TEXT_INDENT_2', -83, 'Body Text Indent 2.' + ), + EnumMember( + 'BODY_TEXT_INDENT_3', -84, 'Body Text Indent 3.' + ), + EnumMember( + 'BOOK_TITLE', -265, 'Book Title.' + ), + EnumMember( + 'CAPTION', -35, 'Caption.' + ), + EnumMember( + 'CLOSING', -64, 'Closing.' + ), + EnumMember( + 'COMMENT_REFERENCE', -40, 'Comment Reference.' + ), + EnumMember( + 'COMMENT_TEXT', -31, 'Comment Text.' + ), + EnumMember( + 'DATE', -77, 'Date.' + ), + EnumMember( + 'DEFAULT_PARAGRAPH_FONT', -66, 'Default Paragraph Font.' + ), + EnumMember( + 'EMPHASIS', -89, 'Emphasis.' + ), + EnumMember( + 'ENDNOTE_REFERENCE', -43, 'Endnote Reference.' + ), + EnumMember( + 'ENDNOTE_TEXT', -44, 'Endnote Text.' + ), + EnumMember( + 'ENVELOPE_ADDRESS', -37, 'Envelope Address.' + ), + EnumMember( + 'ENVELOPE_RETURN', -38, 'Envelope Return.' + ), + EnumMember( + 'FOOTER', -33, 'Footer.' + ), + EnumMember( + 'FOOTNOTE_REFERENCE', -39, 'Footnote Reference.' + ), + EnumMember( + 'FOOTNOTE_TEXT', -30, 'Footnote Text.' + ), + EnumMember( + 'HEADER', -32, 'Header.' + ), + EnumMember( + 'HEADING_1', -2, 'Heading 1.' + ), + EnumMember( + 'HEADING_2', -3, 'Heading 2.' + ), + EnumMember( + 'HEADING_3', -4, 'Heading 3.' + ), + EnumMember( + 'HEADING_4', -5, 'Heading 4.' + ), + EnumMember( + 'HEADING_5', -6, 'Heading 5.' + ), + EnumMember( + 'HEADING_6', -7, 'Heading 6.' + ), + EnumMember( + 'HEADING_7', -8, 'Heading 7.' + ), + EnumMember( + 'HEADING_8', -9, 'Heading 8.' + ), + EnumMember( + 'HEADING_9', -10, 'Heading 9.' + ), + EnumMember( + 'HTML_ACRONYM', -96, 'HTML Acronym.' + ), + EnumMember( + 'HTML_ADDRESS', -97, 'HTML Address.' + ), + EnumMember( + 'HTML_CITE', -98, 'HTML Cite.' + ), + EnumMember( + 'HTML_CODE', -99, 'HTML Code.' + ), + EnumMember( + 'HTML_DFN', -100, 'HTML Definition.' + ), + EnumMember( + 'HTML_KBD', -101, 'HTML Keyboard.' + ), + EnumMember( + 'HTML_NORMAL', -95, 'Normal (Web).' + ), + EnumMember( + 'HTML_PRE', -102, 'HTML Preformatted.' + ), + EnumMember( + 'HTML_SAMP', -103, 'HTML Sample.' + ), + EnumMember( + 'HTML_TT', -104, 'HTML Typewriter.' + ), + EnumMember( + 'HTML_VAR', -105, 'HTML Variable.' + ), + EnumMember( + 'HYPERLINK', -86, 'Hyperlink.' + ), + EnumMember( + 'HYPERLINK_FOLLOWED', -87, 'Followed Hyperlink.' + ), + EnumMember( + 'INDEX_1', -11, 'Index 1.' + ), + EnumMember( + 'INDEX_2', -12, 'Index 2.' + ), + EnumMember( + 'INDEX_3', -13, 'Index 3.' + ), + EnumMember( + 'INDEX_4', -14, 'Index 4.' + ), + EnumMember( + 'INDEX_5', -15, 'Index 5.' + ), + EnumMember( + 'INDEX_6', -16, 'Index 6.' + ), + EnumMember( + 'INDEX_7', -17, 'Index 7.' + ), + EnumMember( + 'INDEX_8', -18, 'Index 8.' + ), + EnumMember( + 'INDEX_9', -19, 'Index 9.' + ), + EnumMember( + 'INDEX_HEADING', -34, 'Index Heading' + ), + EnumMember( + 'INTENSE_EMPHASIS', -262, 'Intense Emphasis.' + ), + EnumMember( + 'INTENSE_QUOTE', -182, 'Intense Quote.' + ), + EnumMember( + 'INTENSE_REFERENCE', -264, 'Intense Reference.' + ), + EnumMember( + 'LINE_NUMBER', -41, 'Line Number.' + ), + EnumMember( + 'LIST', -48, 'List.' + ), + EnumMember( + 'LIST_2', -51, 'List 2.' + ), + EnumMember( + 'LIST_3', -52, 'List 3.' + ), + EnumMember( + 'LIST_4', -53, 'List 4.' + ), + EnumMember( + 'LIST_5', -54, 'List 5.' + ), + EnumMember( + 'LIST_BULLET', -49, 'List Bullet.' + ), + EnumMember( + 'LIST_BULLET_2', -55, 'List Bullet 2.' + ), + EnumMember( + 'LIST_BULLET_3', -56, 'List Bullet 3.' + ), + EnumMember( + 'LIST_BULLET_4', -57, 'List Bullet 4.' + ), + EnumMember( + 'LIST_BULLET_5', -58, 'List Bullet 5.' + ), + EnumMember( + 'LIST_CONTINUE', -69, 'List Continue.' + ), + EnumMember( + 'LIST_CONTINUE_2', -70, 'List Continue 2.' + ), + EnumMember( + 'LIST_CONTINUE_3', -71, 'List Continue 3.' + ), + EnumMember( + 'LIST_CONTINUE_4', -72, 'List Continue 4.' + ), + EnumMember( + 'LIST_CONTINUE_5', -73, 'List Continue 5.' + ), + EnumMember( + 'LIST_NUMBER', -50, 'List Number.' + ), + EnumMember( + 'LIST_NUMBER_2', -59, 'List Number 2.' + ), + EnumMember( + 'LIST_NUMBER_3', -60, 'List Number 3.' + ), + EnumMember( + 'LIST_NUMBER_4', -61, 'List Number 4.' + ), + EnumMember( + 'LIST_NUMBER_5', -62, 'List Number 5.' + ), + EnumMember( + 'LIST_PARAGRAPH', -180, 'List Paragraph.' + ), + EnumMember( + 'MACRO_TEXT', -46, 'Macro Text.' + ), + EnumMember( + 'MESSAGE_HEADER', -74, 'Message Header.' + ), + EnumMember( + 'NAV_PANE', -90, 'Document Map.' + ), + EnumMember( + 'NORMAL', -1, 'Normal.' + ), + EnumMember( + 'NORMAL_INDENT', -29, 'Normal Indent.' + ), + EnumMember( + 'NORMAL_OBJECT', -158, 'Normal (applied to an object).' + ), + EnumMember( + 'NORMAL_TABLE', -106, 'Normal (applied within a table).' + ), + EnumMember( + 'NOTE_HEADING', -80, 'Note Heading.' + ), + EnumMember( + 'PAGE_NUMBER', -42, 'Page Number.' + ), + EnumMember( + 'PLAIN_TEXT', -91, 'Plain Text.' + ), + EnumMember( + 'QUOTE', -181, 'Quote.' + ), + EnumMember( + 'SALUTATION', -76, 'Salutation.' + ), + EnumMember( + 'SIGNATURE', -65, 'Signature.' + ), + EnumMember( + 'STRONG', -88, 'Strong.' + ), + EnumMember( + 'SUBTITLE', -75, 'Subtitle.' + ), + EnumMember( + 'SUBTLE_EMPHASIS', -261, 'Subtle Emphasis.' + ), + EnumMember( + 'SUBTLE_REFERENCE', -263, 'Subtle Reference.' + ), + EnumMember( + 'TABLE_COLORFUL_GRID', -172, 'Colorful Grid.' + ), + EnumMember( + 'TABLE_COLORFUL_LIST', -171, 'Colorful List.' + ), + EnumMember( + 'TABLE_COLORFUL_SHADING', -170, 'Colorful Shading.' + ), + EnumMember( + 'TABLE_DARK_LIST', -169, 'Dark List.' + ), + EnumMember( + 'TABLE_LIGHT_GRID', -161, 'Light Grid.' + ), + EnumMember( + 'TABLE_LIGHT_GRID_ACCENT_1', -175, 'Light Grid Accent 1.' + ), + EnumMember( + 'TABLE_LIGHT_LIST', -160, 'Light List.' + ), + EnumMember( + 'TABLE_LIGHT_LIST_ACCENT_1', -174, 'Light List Accent 1.' + ), + EnumMember( + 'TABLE_LIGHT_SHADING', -159, 'Light Shading.' + ), + EnumMember( + 'TABLE_LIGHT_SHADING_ACCENT_1', -173, 'Light Shading Accent 1.' + ), + EnumMember( + 'TABLE_MEDIUM_GRID_1', -166, 'Medium Grid 1.' + ), + EnumMember( + 'TABLE_MEDIUM_GRID_2', -167, 'Medium Grid 2.' + ), + EnumMember( + 'TABLE_MEDIUM_GRID_3', -168, 'Medium Grid 3.' + ), + EnumMember( + 'TABLE_MEDIUM_LIST_1', -164, 'Medium List 1.' + ), + EnumMember( + 'TABLE_MEDIUM_LIST_1_ACCENT_1', -178, 'Medium List 1 Accent 1.' + ), + EnumMember( + 'TABLE_MEDIUM_LIST_2', -165, 'Medium List 2.' + ), + EnumMember( + 'TABLE_MEDIUM_SHADING_1', -162, 'Medium Shading 1.' + ), + EnumMember( + 'TABLE_MEDIUM_SHADING_1_ACCENT_1', -176, + 'Medium Shading 1 Accent 1.' + ), + EnumMember( + 'TABLE_MEDIUM_SHADING_2', -163, 'Medium Shading 2.' + ), + EnumMember( + 'TABLE_MEDIUM_SHADING_2_ACCENT_1', -177, + 'Medium Shading 2 Accent 1.' + ), + EnumMember( + 'TABLE_OF_AUTHORITIES', -45, 'Table of Authorities.' + ), + EnumMember( + 'TABLE_OF_FIGURES', -36, 'Table of Figures.' + ), + EnumMember( + 'TITLE', -63, 'Title.' + ), + EnumMember( + 'TOAHEADING', -47, 'TOA Heading.' + ), + EnumMember( + 'TOC_1', -20, 'TOC 1.' + ), + EnumMember( + 'TOC_2', -21, 'TOC 2.' + ), + EnumMember( + 'TOC_3', -22, 'TOC 3.' + ), + EnumMember( + 'TOC_4', -23, 'TOC 4.' + ), + EnumMember( + 'TOC_5', -24, 'TOC 5.' + ), + EnumMember( + 'TOC_6', -25, 'TOC 6.' + ), + EnumMember( + 'TOC_7', -26, 'TOC 7.' + ), + EnumMember( + 'TOC_8', -27, 'TOC 8.' + ), + EnumMember( + 'TOC_9', -28, 'TOC 9.' + ), + ) + + +class WD_STYLE_TYPE(XmlEnumeration): + """ + Specifies one of the four style types: paragraph, character, list, or + table. + + Example:: + + from docx import Document + from docx.enum.style import WD_STYLE_TYPE + + styles = Document().styles + assert styles[0].type == WD_STYLE_TYPE.PARAGRAPH + """ + + __ms_name__ = 'WdStyleType' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff196870.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'CHARACTER', 2, 'character', 'Character style.' + ), + XmlMappedEnumMember( + 'LIST', 4, 'numbering', 'List style.' + ), + XmlMappedEnumMember( + 'PARAGRAPH', 1, 'paragraph', 'Paragraph style.' + ), + XmlMappedEnumMember( + 'TABLE', 3, 'table', 'Table style.' + ), + ) diff --git a/build/lib/docx/enum/table.py b/build/lib/docx/enum/table.py new file mode 100644 index 000000000..eedab082e --- /dev/null +++ b/build/lib/docx/enum/table.py @@ -0,0 +1,146 @@ +# encoding: utf-8 + +""" +Enumerations related to tables in WordprocessingML files +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .base import ( + alias, Enumeration, EnumMember, XmlEnumeration, XmlMappedEnumMember +) + + +@alias('WD_ALIGN_VERTICAL') +class WD_CELL_VERTICAL_ALIGNMENT(XmlEnumeration): + """ + alias: **WD_ALIGN_VERTICAL** + + Specifies the vertical alignment of text in one or more cells of a table. + + Example:: + + from docx.enum.table import WD_ALIGN_VERTICAL + + table = document.add_table(3, 3) + table.cell(0, 0).vertical_alignment = WD_ALIGN_VERTICAL.BOTTOM + """ + + __ms_name__ = 'WdCellVerticalAlignment' + + __url__ = 'https://msdn.microsoft.com/en-us/library/office/ff193345.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'TOP', 0, 'top', 'Text is aligned to the top border of the cell.' + ), + XmlMappedEnumMember( + 'CENTER', 1, 'center', 'Text is aligned to the center of the cel' + 'l.' + ), + XmlMappedEnumMember( + 'BOTTOM', 3, 'bottom', 'Text is aligned to the bottom border of ' + 'the cell.' + ), + XmlMappedEnumMember( + 'BOTH', 101, 'both', 'This is an option in the OpenXml spec, but' + ' not in Word itself. It\'s not clear what Word behavior this se' + 'tting produces. If you find out please let us know and we\'ll u' + 'pdate this documentation. Otherwise, probably best to avoid thi' + 's option.' + ), + ) + + +@alias('WD_ROW_HEIGHT') +class WD_ROW_HEIGHT_RULE(XmlEnumeration): + """ + alias: **WD_ROW_HEIGHT** + + Specifies the rule for determining the height of a table row + + Example:: + + from docx.enum.table import WD_ROW_HEIGHT_RULE + + table = document.add_table(3, 3) + table.rows[0].height_rule = WD_ROW_HEIGHT_RULE.EXACTLY + """ + + __ms_name__ = "WdRowHeightRule" + + __url__ = 'https://msdn.microsoft.com/en-us/library/office/ff193620.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'AUTO', 0, 'auto', 'The row height is adjusted to accommodate th' + 'e tallest value in the row.' + ), + XmlMappedEnumMember( + 'AT_LEAST', 1, 'atLeast', 'The row height is at least a minimum ' + 'specified value.' + ), + XmlMappedEnumMember( + 'EXACTLY', 2, 'exact', 'The row height is an exact value.' + ), + ) + + +class WD_TABLE_ALIGNMENT(XmlEnumeration): + """ + Specifies table justification type. + + Example:: + + from docx.enum.table import WD_TABLE_ALIGNMENT + + table = document.add_table(3, 3) + table.alignment = WD_TABLE_ALIGNMENT.CENTER + """ + + __ms_name__ = 'WdRowAlignment' + + __url__ = ' http://office.microsoft.com/en-us/word-help/HV080607259.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'LEFT', 0, 'left', 'Left-aligned' + ), + XmlMappedEnumMember( + 'CENTER', 1, 'center', 'Center-aligned.' + ), + XmlMappedEnumMember( + 'RIGHT', 2, 'right', 'Right-aligned.' + ), + ) + + +class WD_TABLE_DIRECTION(Enumeration): + """ + Specifies the direction in which an application orders cells in the + specified table or row. + + Example:: + + from docx.enum.table import WD_TABLE_DIRECTION + + table = document.add_table(3, 3) + table.direction = WD_TABLE_DIRECTION.RTL + """ + + __ms_name__ = 'WdTableDirection' + + __url__ = ' http://msdn.microsoft.com/en-us/library/ff835141.aspx' + + __members__ = ( + EnumMember( + 'LTR', 0, 'The table or row is arranged with the first column ' + 'in the leftmost position.' + ), + EnumMember( + 'RTL', 1, 'The table or row is arranged with the first column ' + 'in the rightmost position.' + ), + ) diff --git a/build/lib/docx/enum/text.py b/build/lib/docx/enum/text.py new file mode 100644 index 000000000..f4111eb92 --- /dev/null +++ b/build/lib/docx/enum/text.py @@ -0,0 +1,351 @@ +# encoding: utf-8 + +""" +Enumerations related to text in WordprocessingML files +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from .base import alias, EnumMember, XmlEnumeration, XmlMappedEnumMember + + +@alias('WD_ALIGN_PARAGRAPH') +class WD_PARAGRAPH_ALIGNMENT(XmlEnumeration): + """ + alias: **WD_ALIGN_PARAGRAPH** + + Specifies paragraph justification type. + + Example:: + + from docx.enum.text import WD_ALIGN_PARAGRAPH + + paragraph = document.add_paragraph() + paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER + """ + + __ms_name__ = 'WdParagraphAlignment' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff835817.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'LEFT', 0, 'left', 'Left-aligned' + ), + XmlMappedEnumMember( + 'CENTER', 1, 'center', 'Center-aligned.' + ), + XmlMappedEnumMember( + 'RIGHT', 2, 'right', 'Right-aligned.' + ), + XmlMappedEnumMember( + 'JUSTIFY', 3, 'both', 'Fully justified.' + ), + XmlMappedEnumMember( + 'DISTRIBUTE', 4, 'distribute', 'Paragraph characters are distrib' + 'uted to fill the entire width of the paragraph.' + ), + XmlMappedEnumMember( + 'JUSTIFY_MED', 5, 'mediumKashida', 'Justified with a medium char' + 'acter compression ratio.' + ), + XmlMappedEnumMember( + 'JUSTIFY_HI', 7, 'highKashida', 'Justified with a high character' + ' compression ratio.' + ), + XmlMappedEnumMember( + 'JUSTIFY_LOW', 8, 'lowKashida', 'Justified with a low character ' + 'compression ratio.' + ), + XmlMappedEnumMember( + 'THAI_JUSTIFY', 9, 'thaiDistribute', 'Justified according to Tha' + 'i formatting layout.' + ), + ) + + +class WD_BREAK_TYPE(object): + """ + Corresponds to WdBreakType enumeration + http://msdn.microsoft.com/en-us/library/office/ff195905.aspx + """ + COLUMN = 8 + LINE = 6 + LINE_CLEAR_LEFT = 9 + LINE_CLEAR_RIGHT = 10 + LINE_CLEAR_ALL = 11 # added for consistency, not in MS version + PAGE = 7 + SECTION_CONTINUOUS = 3 + SECTION_EVEN_PAGE = 4 + SECTION_NEXT_PAGE = 2 + SECTION_ODD_PAGE = 5 + TEXT_WRAPPING = 11 + +WD_BREAK = WD_BREAK_TYPE + + +@alias('WD_COLOR') +class WD_COLOR_INDEX(XmlEnumeration): + """ + Specifies a standard preset color to apply. Used for font highlighting and + perhaps other applications. + """ + + __ms_name__ = 'WdColorIndex' + + __url__ = 'https://msdn.microsoft.com/EN-US/library/office/ff195343.aspx' + + __members__ = ( + XmlMappedEnumMember( + None, None, None, 'Color is inherited from the style hierarchy.' + ), + XmlMappedEnumMember( + 'AUTO', 0, 'default', 'Automatic color. Default; usually black.' + ), + XmlMappedEnumMember( + 'BLACK', 1, 'black', 'Black color.' + ), + XmlMappedEnumMember( + 'BLUE', 2, 'blue', 'Blue color' + ), + XmlMappedEnumMember( + 'BRIGHT_GREEN', 4, 'green', 'Bright green color.' + ), + XmlMappedEnumMember( + 'DARK_BLUE', 9, 'darkBlue', 'Dark blue color.' + ), + XmlMappedEnumMember( + 'DARK_RED', 13, 'darkRed', 'Dark red color.' + ), + XmlMappedEnumMember( + 'DARK_YELLOW', 14, 'darkYellow', 'Dark yellow color.' + ), + XmlMappedEnumMember( + 'GRAY_25', 16, 'lightGray', '25% shade of gray color.' + ), + XmlMappedEnumMember( + 'GRAY_50', 15, 'darkGray', '50% shade of gray color.' + ), + XmlMappedEnumMember( + 'GREEN', 11, 'darkGreen', 'Green color.' + ), + XmlMappedEnumMember( + 'PINK', 5, 'magenta', 'Pink color.' + ), + XmlMappedEnumMember( + 'RED', 6, 'red', 'Red color.' + ), + XmlMappedEnumMember( + 'TEAL', 10, 'darkCyan', 'Teal color.' + ), + XmlMappedEnumMember( + 'TURQUOISE', 3, 'cyan', 'Turquoise color.' + ), + XmlMappedEnumMember( + 'VIOLET', 12, 'darkMagenta', 'Violet color.' + ), + XmlMappedEnumMember( + 'WHITE', 8, 'white', 'White color.' + ), + XmlMappedEnumMember( + 'YELLOW', 7, 'yellow', 'Yellow color.' + ), + ) + + +class WD_LINE_SPACING(XmlEnumeration): + """ + Specifies a line spacing format to be applied to a paragraph. + + Example:: + + from docx.enum.text import WD_LINE_SPACING + + paragraph = document.add_paragraph() + paragraph.line_spacing_rule = WD_LINE_SPACING.EXACTLY + """ + + __ms_name__ = 'WdLineSpacing' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff844910.aspx' + + __members__ = ( + EnumMember( + 'ONE_POINT_FIVE', 1, 'Space-and-a-half line spacing.' + ), + XmlMappedEnumMember( + 'AT_LEAST', 3, 'atLeast', 'Line spacing is always at least the s' + 'pecified amount. The amount is specified separately.' + ), + EnumMember( + 'DOUBLE', 2, 'Double spaced.' + ), + XmlMappedEnumMember( + 'EXACTLY', 4, 'exact', 'Line spacing is exactly the specified am' + 'ount. The amount is specified separately.' + ), + XmlMappedEnumMember( + 'MULTIPLE', 5, 'auto', 'Line spacing is specified as a multiple ' + 'of line heights. Changing the font size will change the line sp' + 'acing proportionately.' + ), + EnumMember( + 'SINGLE', 0, 'Single spaced (default).' + ), + ) + + +class WD_TAB_ALIGNMENT(XmlEnumeration): + """ + Specifies the tab stop alignment to apply. + """ + + __ms_name__ = 'WdTabAlignment' + + __url__ = 'https://msdn.microsoft.com/EN-US/library/office/ff195609.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'LEFT', 0, 'left', 'Left-aligned.' + ), + XmlMappedEnumMember( + 'CENTER', 1, 'center', 'Center-aligned.' + ), + XmlMappedEnumMember( + 'RIGHT', 2, 'right', 'Right-aligned.' + ), + XmlMappedEnumMember( + 'DECIMAL', 3, 'decimal', 'Decimal-aligned.' + ), + XmlMappedEnumMember( + 'BAR', 4, 'bar', 'Bar-aligned.' + ), + XmlMappedEnumMember( + 'LIST', 6, 'list', 'List-aligned. (deprecated)' + ), + XmlMappedEnumMember( + 'CLEAR', 101, 'clear', 'Clear an inherited tab stop.' + ), + XmlMappedEnumMember( + 'END', 102, 'end', 'Right-aligned. (deprecated)' + ), + XmlMappedEnumMember( + 'NUM', 103, 'num', 'Left-aligned. (deprecated)' + ), + XmlMappedEnumMember( + 'START', 104, 'start', 'Left-aligned. (deprecated)' + ), + ) + + +class WD_TAB_LEADER(XmlEnumeration): + """ + Specifies the character to use as the leader with formatted tabs. + """ + + __ms_name__ = 'WdTabLeader' + + __url__ = 'https://msdn.microsoft.com/en-us/library/office/ff845050.aspx' + + __members__ = ( + XmlMappedEnumMember( + 'SPACES', 0, 'none', 'Spaces. Default.' + ), + XmlMappedEnumMember( + 'DOTS', 1, 'dot', 'Dots.' + ), + XmlMappedEnumMember( + 'DASHES', 2, 'hyphen', 'Dashes.' + ), + XmlMappedEnumMember( + 'LINES', 3, 'underscore', 'Double lines.' + ), + XmlMappedEnumMember( + 'HEAVY', 4, 'heavy', 'A heavy line.' + ), + XmlMappedEnumMember( + 'MIDDLE_DOT', 5, 'middleDot', 'A vertically-centered dot.' + ), + ) + + +class WD_UNDERLINE(XmlEnumeration): + """ + Specifies the style of underline applied to a run of characters. + """ + + __ms_name__ = 'WdUnderline' + + __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff822388.aspx' + + __members__ = ( + XmlMappedEnumMember( + None, None, None, 'Inherit underline setting from containing par' + 'agraph.' + ), + XmlMappedEnumMember( + 'NONE', 0, 'none', 'No underline. This setting overrides any inh' + 'erited underline value, so can be used to remove underline from' + ' a run that inherits underlining from its containing paragraph.' + ' Note this is not the same as assigning |None| to Run.underline' + '. |None| is a valid assignment value, but causes the run to inh' + 'erit its underline value. Assigning ``WD_UNDERLINE.NONE`` cause' + 's underlining to be unconditionally turned off.' + ), + XmlMappedEnumMember( + 'SINGLE', 1, 'single', 'A single line. Note that this setting is' + 'write-only in the sense that |True| (rather than ``WD_UNDERLINE' + '.SINGLE``) is returned for a run having this setting.' + ), + XmlMappedEnumMember( + 'WORDS', 2, 'words', 'Underline individual words only.' + ), + XmlMappedEnumMember( + 'DOUBLE', 3, 'double', 'A double line.' + ), + XmlMappedEnumMember( + 'DOTTED', 4, 'dotted', 'Dots.' + ), + XmlMappedEnumMember( + 'THICK', 6, 'thick', 'A single thick line.' + ), + XmlMappedEnumMember( + 'DASH', 7, 'dash', 'Dashes.' + ), + XmlMappedEnumMember( + 'DOT_DASH', 9, 'dotDash', 'Alternating dots and dashes.' + ), + XmlMappedEnumMember( + 'DOT_DOT_DASH', 10, 'dotDotDash', 'An alternating dot-dot-dash p' + 'attern.' + ), + XmlMappedEnumMember( + 'WAVY', 11, 'wave', 'A single wavy line.' + ), + XmlMappedEnumMember( + 'DOTTED_HEAVY', 20, 'dottedHeavy', 'Heavy dots.' + ), + XmlMappedEnumMember( + 'DASH_HEAVY', 23, 'dashedHeavy', 'Heavy dashes.' + ), + XmlMappedEnumMember( + 'DOT_DASH_HEAVY', 25, 'dashDotHeavy', 'Alternating heavy dots an' + 'd heavy dashes.' + ), + XmlMappedEnumMember( + 'DOT_DOT_DASH_HEAVY', 26, 'dashDotDotHeavy', 'An alternating hea' + 'vy dot-dot-dash pattern.' + ), + XmlMappedEnumMember( + 'WAVY_HEAVY', 27, 'wavyHeavy', 'A heavy wavy line.' + ), + XmlMappedEnumMember( + 'DASH_LONG', 39, 'dashLong', 'Long dashes.' + ), + XmlMappedEnumMember( + 'WAVY_DOUBLE', 43, 'wavyDouble', 'A double wavy line.' + ), + XmlMappedEnumMember( + 'DASH_LONG_HEAVY', 55, 'dashLongHeavy', 'Long heavy dashes.' + ), + ) diff --git a/build/lib/docx/exceptions.py b/build/lib/docx/exceptions.py new file mode 100644 index 000000000..7a8b99c81 --- /dev/null +++ b/build/lib/docx/exceptions.py @@ -0,0 +1,27 @@ +# encoding: utf-8 + +""" +Exceptions used with python-docx. + +The base exception class is PythonDocxError. +""" + + +class PythonDocxError(Exception): + """ + Generic error class. + """ + + +class InvalidSpanError(PythonDocxError): + """ + Raised when an invalid merge region is specified in a request to merge + table cells. + """ + + +class InvalidXmlError(PythonDocxError): + """ + Raised when invalid XML is encountered, such as on attempt to access a + missing required child element + """ diff --git a/build/lib/docx/image/__init__.py b/build/lib/docx/image/__init__.py new file mode 100644 index 000000000..8ab3ada68 --- /dev/null +++ b/build/lib/docx/image/__init__.py @@ -0,0 +1,29 @@ +# encoding: utf-8 + +""" +Provides objects that can characterize image streams as to content type and +size, as a required step in including them in a document. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from docx.image.bmp import Bmp +from docx.image.gif import Gif +from docx.image.jpeg import Exif, Jfif +from docx.image.png import Png +from docx.image.tiff import Tiff + + +SIGNATURES = ( + # class, offset, signature_bytes + (Png, 0, b'\x89PNG\x0D\x0A\x1A\x0A'), + (Jfif, 6, b'JFIF'), + (Exif, 6, b'Exif'), + (Gif, 0, b'GIF87a'), + (Gif, 0, b'GIF89a'), + (Tiff, 0, b'MM\x00*'), # big-endian (Motorola) TIFF + (Tiff, 0, b'II*\x00'), # little-endian (Intel) TIFF + (Bmp, 0, b'BM'), +) diff --git a/build/lib/docx/image/bmp.py b/build/lib/docx/image/bmp.py new file mode 100644 index 000000000..d22f25871 --- /dev/null +++ b/build/lib/docx/image/bmp.py @@ -0,0 +1,56 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from .constants import MIME_TYPE +from .helpers import LITTLE_ENDIAN, StreamReader +from .image import BaseImageHeader + + +class Bmp(BaseImageHeader): + """ + Image header parser for BMP images + """ + @classmethod + def from_stream(cls, stream): + """ + Return |Bmp| instance having header properties parsed from the BMP + image in *stream*. + """ + stream_rdr = StreamReader(stream, LITTLE_ENDIAN) + + px_width = stream_rdr.read_long(0x12) + px_height = stream_rdr.read_long(0x16) + + horz_px_per_meter = stream_rdr.read_long(0x26) + vert_px_per_meter = stream_rdr.read_long(0x2A) + + horz_dpi = cls._dpi(horz_px_per_meter) + vert_dpi = cls._dpi(vert_px_per_meter) + + return cls(px_width, px_height, horz_dpi, vert_dpi) + + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/bmp` for + BMP images. + """ + return MIME_TYPE.BMP + + @property + def default_ext(self): + """ + Default filename extension, always 'bmp' for BMP images. + """ + return 'bmp' + + @staticmethod + def _dpi(px_per_meter): + """ + Return the integer pixels per inch from *px_per_meter*, defaulting to + 96 if *px_per_meter* is zero. + """ + if px_per_meter == 0: + return 96 + return int(round(px_per_meter * 0.0254)) diff --git a/build/lib/docx/image/constants.py b/build/lib/docx/image/constants.py new file mode 100644 index 000000000..90b469705 --- /dev/null +++ b/build/lib/docx/image/constants.py @@ -0,0 +1,169 @@ +# encoding: utf-8 + +""" +Constants specific the the image sub-package +""" + + +class JPEG_MARKER_CODE(object): + """ + JPEG marker codes + """ + TEM = b'\x01' + DHT = b'\xC4' + DAC = b'\xCC' + JPG = b'\xC8' + + SOF0 = b'\xC0' + SOF1 = b'\xC1' + SOF2 = b'\xC2' + SOF3 = b'\xC3' + SOF5 = b'\xC5' + SOF6 = b'\xC6' + SOF7 = b'\xC7' + SOF9 = b'\xC9' + SOFA = b'\xCA' + SOFB = b'\xCB' + SOFD = b'\xCD' + SOFE = b'\xCE' + SOFF = b'\xCF' + + RST0 = b'\xD0' + RST1 = b'\xD1' + RST2 = b'\xD2' + RST3 = b'\xD3' + RST4 = b'\xD4' + RST5 = b'\xD5' + RST6 = b'\xD6' + RST7 = b'\xD7' + + SOI = b'\xD8' + EOI = b'\xD9' + SOS = b'\xDA' + DQT = b'\xDB' # Define Quantization Table(s) + DNL = b'\xDC' + DRI = b'\xDD' + DHP = b'\xDE' + EXP = b'\xDF' + + APP0 = b'\xE0' + APP1 = b'\xE1' + APP2 = b'\xE2' + APP3 = b'\xE3' + APP4 = b'\xE4' + APP5 = b'\xE5' + APP6 = b'\xE6' + APP7 = b'\xE7' + APP8 = b'\xE8' + APP9 = b'\xE9' + APPA = b'\xEA' + APPB = b'\xEB' + APPC = b'\xEC' + APPD = b'\xED' + APPE = b'\xEE' + APPF = b'\xEF' + + STANDALONE_MARKERS = ( + TEM, SOI, EOI, RST0, RST1, RST2, RST3, RST4, RST5, RST6, RST7 + ) + + SOF_MARKER_CODES = ( + SOF0, SOF1, SOF2, SOF3, SOF5, SOF6, SOF7, SOF9, SOFA, SOFB, SOFD, + SOFE, SOFF + ) + + marker_names = { + b'\x00': 'UNKNOWN', + b'\xC0': 'SOF0', + b'\xC2': 'SOF2', + b'\xC4': 'DHT', + b'\xDA': 'SOS', # start of scan + b'\xD8': 'SOI', # start of image + b'\xD9': 'EOI', # end of image + b'\xDB': 'DQT', + b'\xE0': 'APP0', + b'\xE1': 'APP1', + b'\xE2': 'APP2', + b'\xED': 'APP13', + b'\xEE': 'APP14', + } + + @classmethod + def is_standalone(cls, marker_code): + return marker_code in cls.STANDALONE_MARKERS + + +class MIME_TYPE(object): + """ + Image content types + """ + BMP = 'image/bmp' + GIF = 'image/gif' + JPEG = 'image/jpeg' + PNG = 'image/png' + TIFF = 'image/tiff' + + +class PNG_CHUNK_TYPE(object): + """ + PNG chunk type names + """ + IHDR = 'IHDR' + pHYs = 'pHYs' + IEND = 'IEND' + + +class TIFF_FLD_TYPE(object): + """ + Tag codes for TIFF Image File Directory (IFD) entries. + """ + BYTE = 1 + ASCII = 2 + SHORT = 3 + LONG = 4 + RATIONAL = 5 + + field_type_names = { + 1: 'BYTE', 2: 'ASCII char', 3: 'SHORT', 4: 'LONG', + 5: 'RATIONAL' + } + + +TIFF_FLD = TIFF_FLD_TYPE + + +class TIFF_TAG(object): + """ + Tag codes for TIFF Image File Directory (IFD) entries. + """ + IMAGE_WIDTH = 0x0100 + IMAGE_LENGTH = 0x0101 + X_RESOLUTION = 0x011A + Y_RESOLUTION = 0x011B + RESOLUTION_UNIT = 0x0128 + + tag_names = { + 0x00FE: 'NewSubfileType', + 0x0100: 'ImageWidth', + 0x0101: 'ImageLength', + 0x0102: 'BitsPerSample', + 0x0103: 'Compression', + 0x0106: 'PhotometricInterpretation', + 0x010E: 'ImageDescription', + 0x010F: 'Make', + 0x0110: 'Model', + 0x0111: 'StripOffsets', + 0x0112: 'Orientation', + 0x0115: 'SamplesPerPixel', + 0x0117: 'StripByteCounts', + 0x011A: 'XResolution', + 0x011B: 'YResolution', + 0x011C: 'PlanarConfiguration', + 0x0128: 'ResolutionUnit', + 0x0131: 'Software', + 0x0132: 'DateTime', + 0x0213: 'YCbCrPositioning', + 0x8769: 'ExifTag', + 0x8825: 'GPS IFD', + 0xC4A5: 'PrintImageMatching', + } diff --git a/build/lib/docx/image/exceptions.py b/build/lib/docx/image/exceptions.py new file mode 100644 index 000000000..f233edc4e --- /dev/null +++ b/build/lib/docx/image/exceptions.py @@ -0,0 +1,23 @@ +# encoding: utf-8 + +""" +Exceptions specific the the image sub-package +""" + + +class InvalidImageStreamError(Exception): + """ + The recognized image stream appears to be corrupted + """ + + +class UnexpectedEndOfFileError(Exception): + """ + EOF was unexpectedly encountered while reading an image stream. + """ + + +class UnrecognizedImageError(Exception): + """ + The provided image stream could not be recognized. + """ diff --git a/build/lib/docx/image/gif.py b/build/lib/docx/image/gif.py new file mode 100644 index 000000000..57f037d80 --- /dev/null +++ b/build/lib/docx/image/gif.py @@ -0,0 +1,47 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from struct import Struct + +from .constants import MIME_TYPE +from .image import BaseImageHeader + + +class Gif(BaseImageHeader): + """ + Image header parser for GIF images. Note that the GIF format does not + support resolution (DPI) information. Both horizontal and vertical DPI + default to 72. + """ + @classmethod + def from_stream(cls, stream): + """ + Return |Gif| instance having header properties parsed from GIF image + in *stream*. + """ + px_width, px_height = cls._dimensions_from_stream(stream) + return cls(px_width, px_height, 72, 72) + + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/gif` for + GIF images. + """ + return MIME_TYPE.GIF + + @property + def default_ext(self): + """ + Default filename extension, always 'gif' for GIF images. + """ + return 'gif' + + @classmethod + def _dimensions_from_stream(cls, stream): + stream.seek(6) + bytes_ = stream.read(4) + struct = Struct('L' + return self._read_int(fmt, base, offset) + + def read_short(self, base, offset=0): + """ + Return the int value of the two bytes at the file position determined + by *base* and *offset*, similarly to ``read_long()`` above. + """ + fmt = b'H' + return self._read_int(fmt, base, offset) + + def read_str(self, char_count, base, offset=0): + """ + Return a string containing the *char_count* bytes at the file + position determined by self._base_offset + *base* + *offset*. + """ + def str_struct(char_count): + format_ = '%ds' % char_count + return Struct(format_) + struct = str_struct(char_count) + chars = self._unpack_item(struct, base, offset) + unicode_str = chars.decode('UTF-8') + return unicode_str + + def seek(self, base, offset=0): + location = self._base_offset + base + offset + self._stream.seek(location) + + def tell(self): + """ + Allow pass-through tell() call + """ + return self._stream.tell() + + def _read_bytes(self, byte_count, base, offset): + self.seek(base, offset) + bytes_ = self._stream.read(byte_count) + if len(bytes_) < byte_count: + raise UnexpectedEndOfFileError + return bytes_ + + def _read_int(self, fmt, base, offset): + struct = Struct(fmt) + return self._unpack_item(struct, base, offset) + + def _unpack_item(self, struct, base, offset): + bytes_ = self._read_bytes(struct.size, base, offset) + return struct.unpack(bytes_)[0] diff --git a/build/lib/docx/image/image.py b/build/lib/docx/image/image.py new file mode 100644 index 000000000..ba2158e72 --- /dev/null +++ b/build/lib/docx/image/image.py @@ -0,0 +1,263 @@ +# encoding: utf-8 + +""" +Provides objects that can characterize image streams as to content type and +size, as a required step in including them in a document. +""" + +from __future__ import absolute_import, division, print_function + +import hashlib +import os + +from ..compat import BytesIO, is_string +from .exceptions import UnrecognizedImageError +from ..shared import Emu, Inches, lazyproperty + + +class Image(object): + """ + Graphical image stream such as JPEG, PNG, or GIF with properties and + methods required by ImagePart. + """ + def __init__(self, blob, filename, image_header): + super(Image, self).__init__() + self._blob = blob + self._filename = filename + self._image_header = image_header + + @classmethod + def from_blob(cls, blob): + """ + Return a new |Image| subclass instance parsed from the image binary + contained in *blob*. + """ + stream = BytesIO(blob) + return cls._from_stream(stream, blob) + + @classmethod + def from_file(cls, image_descriptor): + """ + Return a new |Image| subclass instance loaded from the image file + identified by *image_descriptor*, a path or file-like object. + """ + if is_string(image_descriptor): + path = image_descriptor + with open(path, 'rb') as f: + blob = f.read() + stream = BytesIO(blob) + filename = os.path.basename(path) + else: + stream = image_descriptor + stream.seek(0) + blob = stream.read() + filename = None + return cls._from_stream(stream, blob, filename) + + @property + def blob(self): + """ + The bytes of the image 'file' + """ + return self._blob + + @property + def content_type(self): + """ + MIME content type for this image, e.g. ``'image/jpeg'`` for a JPEG + image + """ + return self._image_header.content_type + + @lazyproperty + def ext(self): + """ + The file extension for the image. If an actual one is available from + a load filename it is used. Otherwise a canonical extension is + assigned based on the content type. Does not contain the leading + period, e.g. 'jpg', not '.jpg'. + """ + return os.path.splitext(self._filename)[1][1:] + + @property + def filename(self): + """ + Original image file name, if loaded from disk, or a generic filename + if loaded from an anonymous stream. + """ + return self._filename + + @property + def px_width(self): + """ + The horizontal pixel dimension of the image + """ + return self._image_header.px_width + + @property + def px_height(self): + """ + The vertical pixel dimension of the image + """ + return self._image_header.px_height + + @property + def horz_dpi(self): + """ + Integer dots per inch for the width of this image. Defaults to 72 + when not present in the file, as is often the case. + """ + return self._image_header.horz_dpi + + @property + def vert_dpi(self): + """ + Integer dots per inch for the height of this image. Defaults to 72 + when not present in the file, as is often the case. + """ + return self._image_header.vert_dpi + + @property + def width(self): + """ + A |Length| value representing the native width of the image, + calculated from the values of `px_width` and `horz_dpi`. + """ + return Inches(self.px_width / self.horz_dpi) + + @property + def height(self): + """ + A |Length| value representing the native height of the image, + calculated from the values of `px_height` and `vert_dpi`. + """ + return Inches(self.px_height / self.vert_dpi) + + def scaled_dimensions(self, width=None, height=None): + """ + Return a (cx, cy) 2-tuple representing the native dimensions of this + image scaled by applying the following rules to *width* and *height*. + If both *width* and *height* are specified, the return value is + (*width*, *height*); no scaling is performed. If only one is + specified, it is used to compute a scaling factor that is then + applied to the unspecified dimension, preserving the aspect ratio of + the image. If both *width* and *height* are |None|, the native + dimensions are returned. The native dimensions are calculated using + the dots-per-inch (dpi) value embedded in the image, defaulting to 72 + dpi if no value is specified, as is often the case. The returned + values are both |Length| objects. + """ + if width is None and height is None: + return self.width, self.height + + if width is None: + scaling_factor = float(height) / float(self.height) + width = round(self.width * scaling_factor) + + if height is None: + scaling_factor = float(width) / float(self.width) + height = round(self.height * scaling_factor) + + return Emu(width), Emu(height) + + @lazyproperty + def sha1(self): + """ + SHA1 hash digest of the image blob + """ + return hashlib.sha1(self._blob).hexdigest() + + @classmethod + def _from_stream(cls, stream, blob, filename=None): + """ + Return an instance of the |Image| subclass corresponding to the + format of the image in *stream*. + """ + image_header = _ImageHeaderFactory(stream) + if filename is None: + filename = 'image.%s' % image_header.default_ext + return cls(blob, filename, image_header) + + +def _ImageHeaderFactory(stream): + """ + Return a |BaseImageHeader| subclass instance that knows how to parse the + headers of the image in *stream*. + """ + from docx.image import SIGNATURES + + def read_32(stream): + stream.seek(0) + return stream.read(32) + + header = read_32(stream) + for cls, offset, signature_bytes in SIGNATURES: + end = offset + len(signature_bytes) + found_bytes = header[offset:end] + if found_bytes == signature_bytes: + return cls.from_stream(stream) + raise UnrecognizedImageError + + +class BaseImageHeader(object): + """ + Base class for image header subclasses like |Jpeg| and |Tiff|. + """ + def __init__(self, px_width, px_height, horz_dpi, vert_dpi): + self._px_width = px_width + self._px_height = px_height + self._horz_dpi = horz_dpi + self._vert_dpi = vert_dpi + + @property + def content_type(self): + """ + Abstract property definition, must be implemented by all subclasses. + """ + msg = ( + 'content_type property must be implemented by all subclasses of ' + 'BaseImageHeader' + ) + raise NotImplementedError(msg) + + @property + def default_ext(self): + """ + Default filename extension for images of this type. An abstract + property definition, must be implemented by all subclasses. + """ + msg = ( + 'default_ext property must be implemented by all subclasses of ' + 'BaseImageHeader' + ) + raise NotImplementedError(msg) + + @property + def px_width(self): + """ + The horizontal pixel dimension of the image + """ + return self._px_width + + @property + def px_height(self): + """ + The vertical pixel dimension of the image + """ + return self._px_height + + @property + def horz_dpi(self): + """ + Integer dots per inch for the width of this image. Defaults to 72 + when not present in the file, as is often the case. + """ + return self._horz_dpi + + @property + def vert_dpi(self): + """ + Integer dots per inch for the height of this image. Defaults to 72 + when not present in the file, as is often the case. + """ + return self._vert_dpi diff --git a/build/lib/docx/image/jpeg.py b/build/lib/docx/image/jpeg.py new file mode 100644 index 000000000..8a263b6c5 --- /dev/null +++ b/build/lib/docx/image/jpeg.py @@ -0,0 +1,498 @@ +# encoding: utf-8 + +""" +Objects related to parsing headers of JPEG image streams, both JFIF and Exif +sub-formats. +""" + +from __future__ import absolute_import, division, print_function + +from ..compat import BytesIO +from .constants import JPEG_MARKER_CODE, MIME_TYPE +from .helpers import BIG_ENDIAN, StreamReader +from .image import BaseImageHeader +from .tiff import Tiff + + +class Jpeg(BaseImageHeader): + """ + Base class for JFIF and EXIF subclasses. + """ + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/jpeg` for + JPEG images. + """ + return MIME_TYPE.JPEG + + @property + def default_ext(self): + """ + Default filename extension, always 'jpg' for JPG images. + """ + return 'jpg' + + +class Exif(Jpeg): + """ + Image header parser for Exif image format + """ + @classmethod + def from_stream(cls, stream): + """ + Return |Exif| instance having header properties parsed from Exif + image in *stream*. + """ + markers = _JfifMarkers.from_stream(stream) + # print('\n%s' % markers) + + px_width = markers.sof.px_width + px_height = markers.sof.px_height + horz_dpi = markers.app1.horz_dpi + vert_dpi = markers.app1.vert_dpi + + return cls(px_width, px_height, horz_dpi, vert_dpi) + + +class Jfif(Jpeg): + """ + Image header parser for JFIF image format + """ + @classmethod + def from_stream(cls, stream): + """ + Return a |Jfif| instance having header properties parsed from image + in *stream*. + """ + markers = _JfifMarkers.from_stream(stream) + + px_width = markers.sof.px_width + px_height = markers.sof.px_height + horz_dpi = markers.app0.horz_dpi + vert_dpi = markers.app0.vert_dpi + + return cls(px_width, px_height, horz_dpi, vert_dpi) + + +class _JfifMarkers(object): + """ + Sequence of markers in a JPEG file, perhaps truncated at first SOS marker + for performance reasons. + """ + def __init__(self, markers): + super(_JfifMarkers, self).__init__() + self._markers = list(markers) + + def __str__(self): # pragma: no cover + """ + Returns a tabular listing of the markers in this instance, which can + be handy for debugging and perhaps other uses. + """ + header = ' offset seglen mc name\n======= ====== == =====' + tmpl = '%7d %6d %02X %s' + rows = [] + for marker in self._markers: + rows.append(tmpl % ( + marker.offset, marker.segment_length, + ord(marker.marker_code), marker.name + )) + lines = [header] + rows + return '\n'.join(lines) + + @classmethod + def from_stream(cls, stream): + """ + Return a |_JfifMarkers| instance containing a |_JfifMarker| subclass + instance for each marker in *stream*. + """ + marker_parser = _MarkerParser.from_stream(stream) + markers = [] + for marker in marker_parser.iter_markers(): + markers.append(marker) + if marker.marker_code == JPEG_MARKER_CODE.SOS: + break + return cls(markers) + + @property + def app0(self): + """ + First APP0 marker in image markers. + """ + for m in self._markers: + if m.marker_code == JPEG_MARKER_CODE.APP0: + return m + raise KeyError('no APP0 marker in image') + + @property + def app1(self): + """ + First APP1 marker in image markers. + """ + for m in self._markers: + if m.marker_code == JPEG_MARKER_CODE.APP1: + return m + raise KeyError('no APP1 marker in image') + + @property + def sof(self): + """ + First start of frame (SOFn) marker in this sequence. + """ + for m in self._markers: + if m.marker_code in JPEG_MARKER_CODE.SOF_MARKER_CODES: + return m + raise KeyError('no start of frame (SOFn) marker in image') + + +class _MarkerParser(object): + """ + Service class that knows how to parse a JFIF stream and iterate over its + markers. + """ + def __init__(self, stream_reader): + super(_MarkerParser, self).__init__() + self._stream = stream_reader + + @classmethod + def from_stream(cls, stream): + """ + Return a |_MarkerParser| instance to parse JFIF markers from + *stream*. + """ + stream_reader = StreamReader(stream, BIG_ENDIAN) + return cls(stream_reader) + + def iter_markers(self): + """ + Generate a (marker_code, segment_offset) 2-tuple for each marker in + the JPEG *stream*, in the order they occur in the stream. + """ + marker_finder = _MarkerFinder.from_stream(self._stream) + start = 0 + marker_code = None + while marker_code != JPEG_MARKER_CODE.EOI: + marker_code, segment_offset = marker_finder.next(start) + marker = _MarkerFactory( + marker_code, self._stream, segment_offset + ) + yield marker + start = segment_offset + marker.segment_length + + +class _MarkerFinder(object): + """ + Service class that knows how to find the next JFIF marker in a stream. + """ + def __init__(self, stream): + super(_MarkerFinder, self).__init__() + self._stream = stream + + @classmethod + def from_stream(cls, stream): + """ + Return a |_MarkerFinder| instance to find JFIF markers in *stream*. + """ + return cls(stream) + + def next(self, start): + """ + Return a (marker_code, segment_offset) 2-tuple identifying and + locating the first marker in *stream* occuring after offset *start*. + The returned *segment_offset* points to the position immediately + following the 2-byte marker code, the start of the marker segment, + for those markers that have a segment. + """ + position = start + while True: + # skip over any non-\xFF bytes + position = self._offset_of_next_ff_byte(start=position) + # skip over any \xFF padding bytes + position, byte_ = self._next_non_ff_byte(start=position+1) + # 'FF 00' sequence is not a marker, start over if found + if byte_ == b'\x00': + continue + # this is a marker, gather return values and break out of scan + marker_code, segment_offset = byte_, position+1 + break + return marker_code, segment_offset + + def _next_non_ff_byte(self, start): + """ + Return an offset, byte 2-tuple for the next byte in *stream* that is + not '\xFF', starting with the byte at offset *start*. If the byte at + offset *start* is not '\xFF', *start* and the returned *offset* will + be the same. + """ + self._stream.seek(start) + byte_ = self._read_byte() + while byte_ == b'\xFF': + byte_ = self._read_byte() + offset_of_non_ff_byte = self._stream.tell() - 1 + return offset_of_non_ff_byte, byte_ + + def _offset_of_next_ff_byte(self, start): + """ + Return the offset of the next '\xFF' byte in *stream* starting with + the byte at offset *start*. Returns *start* if the byte at that + offset is a hex 255; it does not necessarily advance in the stream. + """ + self._stream.seek(start) + byte_ = self._read_byte() + while byte_ != b'\xFF': + byte_ = self._read_byte() + offset_of_ff_byte = self._stream.tell() - 1 + return offset_of_ff_byte + + def _read_byte(self): + """ + Return the next byte read from stream. Raise Exception if stream is + at end of file. + """ + byte_ = self._stream.read(1) + if not byte_: # pragma: no cover + raise Exception('unexpected end of file') + return byte_ + + +def _MarkerFactory(marker_code, stream, offset): + """ + Return |_Marker| or subclass instance appropriate for marker at *offset* + in *stream* having *marker_code*. + """ + if marker_code == JPEG_MARKER_CODE.APP0: + marker_cls = _App0Marker + elif marker_code == JPEG_MARKER_CODE.APP1: + marker_cls = _App1Marker + elif marker_code in JPEG_MARKER_CODE.SOF_MARKER_CODES: + marker_cls = _SofMarker + else: + marker_cls = _Marker + return marker_cls.from_stream(stream, marker_code, offset) + + +class _Marker(object): + """ + Base class for JFIF marker classes. Represents a marker and its segment + occuring in a JPEG byte stream. + """ + def __init__(self, marker_code, offset, segment_length): + super(_Marker, self).__init__() + self._marker_code = marker_code + self._offset = offset + self._segment_length = segment_length + + @classmethod + def from_stream(cls, stream, marker_code, offset): + """ + Return a generic |_Marker| instance for the marker at *offset* in + *stream* having *marker_code*. + """ + if JPEG_MARKER_CODE.is_standalone(marker_code): + segment_length = 0 + else: + segment_length = stream.read_short(offset) + return cls(marker_code, offset, segment_length) + + @property + def marker_code(self): + """ + The single-byte code that identifies the type of this marker, e.g. + ``'\xE0'`` for start of image (SOI). + """ + return self._marker_code + + @property + def name(self): # pragma: no cover + return JPEG_MARKER_CODE.marker_names[self._marker_code] + + @property + def offset(self): # pragma: no cover + return self._offset + + @property + def segment_length(self): + """ + The length in bytes of this marker's segment + """ + return self._segment_length + + +class _App0Marker(_Marker): + """ + Represents a JFIF APP0 marker segment. + """ + def __init__( + self, marker_code, offset, length, density_units, x_density, + y_density): + super(_App0Marker, self).__init__(marker_code, offset, length) + self._density_units = density_units + self._x_density = x_density + self._y_density = y_density + + @property + def horz_dpi(self): + """ + Horizontal dots per inch specified in this marker, defaults to 72 if + not specified. + """ + return self._dpi(self._x_density) + + @property + def vert_dpi(self): + """ + Vertical dots per inch specified in this marker, defaults to 72 if + not specified. + """ + return self._dpi(self._y_density) + + def _dpi(self, density): + """ + Return dots per inch corresponding to *density* value. + """ + if self._density_units == 1: + dpi = density + elif self._density_units == 2: + dpi = int(round(density * 2.54)) + else: + dpi = 72 + return dpi + + @classmethod + def from_stream(cls, stream, marker_code, offset): + """ + Return an |_App0Marker| instance for the APP0 marker at *offset* in + *stream*. + """ + # field off type notes + # ------------------ --- ----- ------------------- + # segment length 0 short + # JFIF identifier 2 5 chr 'JFIF\x00' + # major JPEG version 7 byte typically 1 + # minor JPEG version 8 byte typically 1 or 2 + # density units 9 byte 1=inches, 2=cm + # horz dots per unit 10 short + # vert dots per unit 12 short + # ------------------ --- ----- ------------------- + segment_length = stream.read_short(offset) + density_units = stream.read_byte(offset, 9) + x_density = stream.read_short(offset, 10) + y_density = stream.read_short(offset, 12) + return cls( + marker_code, offset, segment_length, density_units, x_density, + y_density + ) + + +class _App1Marker(_Marker): + """ + Represents a JFIF APP1 (Exif) marker segment. + """ + def __init__(self, marker_code, offset, length, horz_dpi, vert_dpi): + super(_App1Marker, self).__init__(marker_code, offset, length) + self._horz_dpi = horz_dpi + self._vert_dpi = vert_dpi + + @classmethod + def from_stream(cls, stream, marker_code, offset): + """ + Extract the horizontal and vertical dots-per-inch value from the APP1 + header at *offset* in *stream*. + """ + # field off len type notes + # -------------------- --- --- ----- ---------------------------- + # segment length 0 2 short + # Exif identifier 2 6 6 chr 'Exif\x00\x00' + # TIFF byte order 8 2 2 chr 'II'=little 'MM'=big endian + # meaning of universe 10 2 2 chr '*\x00' or '\x00*' depending + # IFD0 off fr/II or MM 10 16 long relative to ...? + # -------------------- --- --- ----- ---------------------------- + segment_length = stream.read_short(offset) + if cls._is_non_Exif_APP1_segment(stream, offset): + return cls(marker_code, offset, segment_length, 72, 72) + tiff = cls._tiff_from_exif_segment(stream, offset, segment_length) + return cls( + marker_code, offset, segment_length, tiff.horz_dpi, tiff.vert_dpi + ) + + @property + def horz_dpi(self): + """ + Horizontal dots per inch specified in this marker, defaults to 72 if + not specified. + """ + return self._horz_dpi + + @property + def vert_dpi(self): + """ + Vertical dots per inch specified in this marker, defaults to 72 if + not specified. + """ + return self._vert_dpi + + @classmethod + def _is_non_Exif_APP1_segment(cls, stream, offset): + """ + Return True if the APP1 segment at *offset* in *stream* is NOT an + Exif segment, as determined by the ``'Exif\x00\x00'`` signature at + offset 2 in the segment. + """ + stream.seek(offset+2) + exif_signature = stream.read(6) + return exif_signature != b'Exif\x00\x00' + + @classmethod + def _tiff_from_exif_segment(cls, stream, offset, segment_length): + """ + Return a |Tiff| instance parsed from the Exif APP1 segment of + *segment_length* at *offset* in *stream*. + """ + # wrap full segment in its own stream and feed to Tiff() + stream.seek(offset+8) + segment_bytes = stream.read(segment_length-8) + substream = BytesIO(segment_bytes) + return Tiff.from_stream(substream) + + +class _SofMarker(_Marker): + """ + Represents a JFIF start of frame (SOFx) marker segment. + """ + def __init__( + self, marker_code, offset, segment_length, px_width, px_height): + super(_SofMarker, self).__init__(marker_code, offset, segment_length) + self._px_width = px_width + self._px_height = px_height + + @classmethod + def from_stream(cls, stream, marker_code, offset): + """ + Return an |_SofMarker| instance for the SOFn marker at *offset* in + stream. + """ + # field off type notes + # ------------------ --- ----- ---------------------------- + # segment length 0 short + # Data precision 2 byte + # Vertical lines 3 short px_height + # Horizontal lines 5 short px_width + # ------------------ --- ----- ---------------------------- + segment_length = stream.read_short(offset) + px_height = stream.read_short(offset, 3) + px_width = stream.read_short(offset, 5) + return cls(marker_code, offset, segment_length, px_width, px_height) + + @property + def px_height(self): + """ + Image height in pixels + """ + return self._px_height + + @property + def px_width(self): + """ + Image width in pixels + """ + return self._px_width diff --git a/build/lib/docx/image/png.py b/build/lib/docx/image/png.py new file mode 100644 index 000000000..c9123f76c --- /dev/null +++ b/build/lib/docx/image/png.py @@ -0,0 +1,303 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from .constants import MIME_TYPE, PNG_CHUNK_TYPE +from .exceptions import InvalidImageStreamError +from .helpers import BIG_ENDIAN, StreamReader +from .image import BaseImageHeader + + +class Png(BaseImageHeader): + """ + Image header parser for PNG images + """ + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/png` for + PNG images. + """ + return MIME_TYPE.PNG + + @property + def default_ext(self): + """ + Default filename extension, always 'png' for PNG images. + """ + return 'png' + + @classmethod + def from_stream(cls, stream): + """ + Return a |Png| instance having header properties parsed from image in + *stream*. + """ + parser = _PngParser.parse(stream) + + px_width = parser.px_width + px_height = parser.px_height + horz_dpi = parser.horz_dpi + vert_dpi = parser.vert_dpi + + return cls(px_width, px_height, horz_dpi, vert_dpi) + + +class _PngParser(object): + """ + Parses a PNG image stream to extract the image properties found in its + chunks. + """ + def __init__(self, chunks): + super(_PngParser, self).__init__() + self._chunks = chunks + + @classmethod + def parse(cls, stream): + """ + Return a |_PngParser| instance containing the header properties + parsed from the PNG image in *stream*. + """ + chunks = _Chunks.from_stream(stream) + return cls(chunks) + + @property + def px_width(self): + """ + The number of pixels in each row of the image. + """ + IHDR = self._chunks.IHDR + return IHDR.px_width + + @property + def px_height(self): + """ + The number of stacked rows of pixels in the image. + """ + IHDR = self._chunks.IHDR + return IHDR.px_height + + @property + def horz_dpi(self): + """ + Integer dots per inch for the width of this image. Defaults to 72 + when not present in the file, as is often the case. + """ + pHYs = self._chunks.pHYs + if pHYs is None: + return 72 + return self._dpi(pHYs.units_specifier, pHYs.horz_px_per_unit) + + @property + def vert_dpi(self): + """ + Integer dots per inch for the height of this image. Defaults to 72 + when not present in the file, as is often the case. + """ + pHYs = self._chunks.pHYs + if pHYs is None: + return 72 + return self._dpi(pHYs.units_specifier, pHYs.vert_px_per_unit) + + @staticmethod + def _dpi(units_specifier, px_per_unit): + """ + Return dots per inch value calculated from *units_specifier* and + *px_per_unit*. + """ + if units_specifier == 1 and px_per_unit: + return int(round(px_per_unit * 0.0254)) + return 72 + + +class _Chunks(object): + """ + Collection of the chunks parsed from a PNG image stream + """ + def __init__(self, chunk_iterable): + super(_Chunks, self).__init__() + self._chunks = list(chunk_iterable) + + @classmethod + def from_stream(cls, stream): + """ + Return a |_Chunks| instance containing the PNG chunks in *stream*. + """ + chunk_parser = _ChunkParser.from_stream(stream) + chunks = [chunk for chunk in chunk_parser.iter_chunks()] + return cls(chunks) + + @property + def IHDR(self): + """ + IHDR chunk in PNG image + """ + match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.IHDR + IHDR = self._find_first(match) + if IHDR is None: + raise InvalidImageStreamError('no IHDR chunk in PNG image') + return IHDR + + @property + def pHYs(self): + """ + pHYs chunk in PNG image, or |None| if not present + """ + match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.pHYs + return self._find_first(match) + + def _find_first(self, match): + """ + Return first chunk in stream order returning True for function + *match*. + """ + for chunk in self._chunks: + if match(chunk): + return chunk + return None + + +class _ChunkParser(object): + """ + Extracts chunks from a PNG image stream + """ + def __init__(self, stream_rdr): + super(_ChunkParser, self).__init__() + self._stream_rdr = stream_rdr + + @classmethod + def from_stream(cls, stream): + """ + Return a |_ChunkParser| instance that can extract the chunks from the + PNG image in *stream*. + """ + stream_rdr = StreamReader(stream, BIG_ENDIAN) + return cls(stream_rdr) + + def iter_chunks(self): + """ + Generate a |_Chunk| subclass instance for each chunk in this parser's + PNG stream, in the order encountered in the stream. + """ + for chunk_type, offset in self._iter_chunk_offsets(): + chunk = _ChunkFactory(chunk_type, self._stream_rdr, offset) + yield chunk + + def _iter_chunk_offsets(self): + """ + Generate a (chunk_type, chunk_offset) 2-tuple for each of the chunks + in the PNG image stream. Iteration stops after the IEND chunk is + returned. + """ + chunk_offset = 8 + while True: + chunk_data_len = self._stream_rdr.read_long(chunk_offset) + chunk_type = self._stream_rdr.read_str(4, chunk_offset, 4) + data_offset = chunk_offset + 8 + yield chunk_type, data_offset + if chunk_type == 'IEND': + break + # incr offset for chunk len long, chunk type, chunk data, and CRC + chunk_offset += (4 + 4 + chunk_data_len + 4) + + +def _ChunkFactory(chunk_type, stream_rdr, offset): + """ + Return a |_Chunk| subclass instance appropriate to *chunk_type* parsed + from *stream_rdr* at *offset*. + """ + chunk_cls_map = { + PNG_CHUNK_TYPE.IHDR: _IHDRChunk, + PNG_CHUNK_TYPE.pHYs: _pHYsChunk, + } + chunk_cls = chunk_cls_map.get(chunk_type, _Chunk) + return chunk_cls.from_offset(chunk_type, stream_rdr, offset) + + +class _Chunk(object): + """ + Base class for specific chunk types. Also serves as the default chunk + type. + """ + def __init__(self, chunk_type): + super(_Chunk, self).__init__() + self._chunk_type = chunk_type + + @classmethod + def from_offset(cls, chunk_type, stream_rdr, offset): + """ + Return a default _Chunk instance that only knows its chunk type. + """ + return cls(chunk_type) + + @property + def type_name(self): + """ + The chunk type name, e.g. 'IHDR', 'pHYs', etc. + """ + return self._chunk_type + + +class _IHDRChunk(_Chunk): + """ + IHDR chunk, contains the image dimensions + """ + def __init__(self, chunk_type, px_width, px_height): + super(_IHDRChunk, self).__init__(chunk_type) + self._px_width = px_width + self._px_height = px_height + + @classmethod + def from_offset(cls, chunk_type, stream_rdr, offset): + """ + Return an _IHDRChunk instance containing the image dimensions + extracted from the IHDR chunk in *stream* at *offset*. + """ + px_width = stream_rdr.read_long(offset) + px_height = stream_rdr.read_long(offset, 4) + return cls(chunk_type, px_width, px_height) + + @property + def px_width(self): + return self._px_width + + @property + def px_height(self): + return self._px_height + + +class _pHYsChunk(_Chunk): + """ + pYHs chunk, contains the image dpi information + """ + def __init__(self, chunk_type, horz_px_per_unit, vert_px_per_unit, + units_specifier): + super(_pHYsChunk, self).__init__(chunk_type) + self._horz_px_per_unit = horz_px_per_unit + self._vert_px_per_unit = vert_px_per_unit + self._units_specifier = units_specifier + + @classmethod + def from_offset(cls, chunk_type, stream_rdr, offset): + """ + Return a _pHYsChunk instance containing the image resolution + extracted from the pHYs chunk in *stream* at *offset*. + """ + horz_px_per_unit = stream_rdr.read_long(offset) + vert_px_per_unit = stream_rdr.read_long(offset, 4) + units_specifier = stream_rdr.read_byte(offset, 8) + return cls( + chunk_type, horz_px_per_unit, vert_px_per_unit, units_specifier + ) + + @property + def horz_px_per_unit(self): + return self._horz_px_per_unit + + @property + def vert_px_per_unit(self): + return self._vert_px_per_unit + + @property + def units_specifier(self): + return self._units_specifier diff --git a/build/lib/docx/image/tiff.py b/build/lib/docx/image/tiff.py new file mode 100644 index 000000000..c38242360 --- /dev/null +++ b/build/lib/docx/image/tiff.py @@ -0,0 +1,345 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from .constants import MIME_TYPE, TIFF_FLD, TIFF_TAG +from .helpers import BIG_ENDIAN, LITTLE_ENDIAN, StreamReader +from .image import BaseImageHeader + + +class Tiff(BaseImageHeader): + """ + Image header parser for TIFF images. Handles both big and little endian + byte ordering. + """ + @property + def content_type(self): + """ + Return the MIME type of this TIFF image, unconditionally the string + ``image/tiff``. + """ + return MIME_TYPE.TIFF + + @property + def default_ext(self): + """ + Default filename extension, always 'tiff' for TIFF images. + """ + return 'tiff' + + @classmethod + def from_stream(cls, stream): + """ + Return a |Tiff| instance containing the properties of the TIFF image + in *stream*. + """ + parser = _TiffParser.parse(stream) + + px_width = parser.px_width + px_height = parser.px_height + horz_dpi = parser.horz_dpi + vert_dpi = parser.vert_dpi + + return cls(px_width, px_height, horz_dpi, vert_dpi) + + +class _TiffParser(object): + """ + Parses a TIFF image stream to extract the image properties found in its + main image file directory (IFD) + """ + def __init__(self, ifd_entries): + super(_TiffParser, self).__init__() + self._ifd_entries = ifd_entries + + @classmethod + def parse(cls, stream): + """ + Return an instance of |_TiffParser| containing the properties parsed + from the TIFF image in *stream*. + """ + stream_rdr = cls._make_stream_reader(stream) + ifd0_offset = stream_rdr.read_long(4) + ifd_entries = _IfdEntries.from_stream(stream_rdr, ifd0_offset) + return cls(ifd_entries) + + @property + def horz_dpi(self): + """ + The horizontal dots per inch value calculated from the XResolution + and ResolutionUnit tags of the IFD; defaults to 72 if those tags are + not present. + """ + return self._dpi(TIFF_TAG.X_RESOLUTION) + + @property + def vert_dpi(self): + """ + The vertical dots per inch value calculated from the XResolution and + ResolutionUnit tags of the IFD; defaults to 72 if those tags are not + present. + """ + return self._dpi(TIFF_TAG.Y_RESOLUTION) + + @property + def px_height(self): + """ + The number of stacked rows of pixels in the image, |None| if the IFD + contains no ``ImageLength`` tag, the expected case when the TIFF is + embeded in an Exif image. + """ + return self._ifd_entries.get(TIFF_TAG.IMAGE_LENGTH) + + @property + def px_width(self): + """ + The number of pixels in each row in the image, |None| if the IFD + contains no ``ImageWidth`` tag, the expected case when the TIFF is + embeded in an Exif image. + """ + return self._ifd_entries.get(TIFF_TAG.IMAGE_WIDTH) + + @classmethod + def _detect_endian(cls, stream): + """ + Return either BIG_ENDIAN or LITTLE_ENDIAN depending on the endian + indicator found in the TIFF *stream* header, either 'MM' or 'II'. + """ + stream.seek(0) + endian_str = stream.read(2) + return BIG_ENDIAN if endian_str == b'MM' else LITTLE_ENDIAN + + def _dpi(self, resolution_tag): + """ + Return the dpi value calculated for *resolution_tag*, which can be + either TIFF_TAG.X_RESOLUTION or TIFF_TAG.Y_RESOLUTION. The + calculation is based on the values of both that tag and the + TIFF_TAG.RESOLUTION_UNIT tag in this parser's |_IfdEntries| instance. + """ + ifd_entries = self._ifd_entries + + if resolution_tag not in ifd_entries: + return 72 + + # resolution unit defaults to inches (2) + resolution_unit = ( + ifd_entries[TIFF_TAG.RESOLUTION_UNIT] + if TIFF_TAG.RESOLUTION_UNIT in ifd_entries else 2 + ) + + if resolution_unit == 1: # aspect ratio only + return 72 + # resolution_unit == 2 for inches, 3 for centimeters + units_per_inch = 1 if resolution_unit == 2 else 2.54 + dots_per_unit = ifd_entries[resolution_tag] + return int(round(dots_per_unit * units_per_inch)) + + @classmethod + def _make_stream_reader(cls, stream): + """ + Return a |StreamReader| instance with wrapping *stream* and having + "endian-ness" determined by the 'MM' or 'II' indicator in the TIFF + stream header. + """ + endian = cls._detect_endian(stream) + return StreamReader(stream, endian) + + +class _IfdEntries(object): + """ + Image File Directory for a TIFF image, having mapping (dict) semantics + allowing "tag" values to be retrieved by tag code. + """ + def __init__(self, entries): + super(_IfdEntries, self).__init__() + self._entries = entries + + def __contains__(self, key): + """ + Provides ``in`` operator, e.g. ``tag in ifd_entries`` + """ + return self._entries.__contains__(key) + + def __getitem__(self, key): + """ + Provides indexed access, e.g. ``tag_value = ifd_entries[tag_code]`` + """ + return self._entries.__getitem__(key) + + @classmethod + def from_stream(cls, stream, offset): + """ + Return a new |_IfdEntries| instance parsed from *stream* starting at + *offset*. + """ + ifd_parser = _IfdParser(stream, offset) + entries = dict((e.tag, e.value) for e in ifd_parser.iter_entries()) + return cls(entries) + + def get(self, tag_code, default=None): + """ + Return value of IFD entry having tag matching *tag_code*, or + *default* if no matching tag found. + """ + return self._entries.get(tag_code, default) + + +class _IfdParser(object): + """ + Service object that knows how to extract directory entries from an Image + File Directory (IFD) + """ + def __init__(self, stream_rdr, offset): + super(_IfdParser, self).__init__() + self._stream_rdr = stream_rdr + self._offset = offset + + def iter_entries(self): + """ + Generate an |_IfdEntry| instance corresponding to each entry in the + directory. + """ + for idx in range(self._entry_count): + dir_entry_offset = self._offset + 2 + (idx*12) + ifd_entry = _IfdEntryFactory(self._stream_rdr, dir_entry_offset) + yield ifd_entry + + @property + def _entry_count(self): + """ + The count of directory entries, read from the top of the IFD header + """ + return self._stream_rdr.read_short(self._offset) + + +def _IfdEntryFactory(stream_rdr, offset): + """ + Return an |_IfdEntry| subclass instance containing the value of the + directory entry at *offset* in *stream_rdr*. + """ + ifd_entry_classes = { + TIFF_FLD.ASCII: _AsciiIfdEntry, + TIFF_FLD.SHORT: _ShortIfdEntry, + TIFF_FLD.LONG: _LongIfdEntry, + TIFF_FLD.RATIONAL: _RationalIfdEntry, + } + field_type = stream_rdr.read_short(offset, 2) + if field_type in ifd_entry_classes: + entry_cls = ifd_entry_classes[field_type] + else: + entry_cls = _IfdEntry + return entry_cls.from_stream(stream_rdr, offset) + + +class _IfdEntry(object): + """ + Base class for IFD entry classes. Subclasses are differentiated by value + type, e.g. ASCII, long int, etc. + """ + def __init__(self, tag_code, value): + super(_IfdEntry, self).__init__() + self._tag_code = tag_code + self._value = value + + @classmethod + def from_stream(cls, stream_rdr, offset): + """ + Return an |_IfdEntry| subclass instance containing the tag and value + of the tag parsed from *stream_rdr* at *offset*. Note this method is + common to all subclasses. Override the ``_parse_value()`` method to + provide distinctive behavior based on field type. + """ + tag_code = stream_rdr.read_short(offset, 0) + value_count = stream_rdr.read_long(offset, 4) + value_offset = stream_rdr.read_long(offset, 8) + value = cls._parse_value( + stream_rdr, offset, value_count, value_offset + ) + return cls(tag_code, value) + + @classmethod + def _parse_value(cls, stream_rdr, offset, value_count, value_offset): + """ + Return the value of this field parsed from *stream_rdr* at *offset*. + Intended to be overridden by subclasses. + """ + return 'UNIMPLEMENTED FIELD TYPE' # pragma: no cover + + @property + def tag(self): + """ + Short int code that identifies this IFD entry + """ + return self._tag_code + + @property + def value(self): + """ + Value of this tag, its type being dependent on the tag. + """ + return self._value + + +class _AsciiIfdEntry(_IfdEntry): + """ + IFD entry having the form of a NULL-terminated ASCII string + """ + @classmethod + def _parse_value(cls, stream_rdr, offset, value_count, value_offset): + """ + Return the ASCII string parsed from *stream_rdr* at *value_offset*. + The length of the string, including a terminating '\x00' (NUL) + character, is in *value_count*. + """ + return stream_rdr.read_str(value_count-1, value_offset) + + +class _ShortIfdEntry(_IfdEntry): + """ + IFD entry expressed as a short (2-byte) integer + """ + @classmethod + def _parse_value(cls, stream_rdr, offset, value_count, value_offset): + """ + Return the short int value contained in the *value_offset* field of + this entry. Only supports single values at present. + """ + if value_count == 1: + return stream_rdr.read_short(offset, 8) + else: # pragma: no cover + return 'Multi-value short integer NOT IMPLEMENTED' + + +class _LongIfdEntry(_IfdEntry): + """ + IFD entry expressed as a long (4-byte) integer + """ + @classmethod + def _parse_value(cls, stream_rdr, offset, value_count, value_offset): + """ + Return the long int value contained in the *value_offset* field of + this entry. Only supports single values at present. + """ + if value_count == 1: + return stream_rdr.read_long(offset, 8) + else: # pragma: no cover + return 'Multi-value long integer NOT IMPLEMENTED' + + +class _RationalIfdEntry(_IfdEntry): + """ + IFD entry expressed as a numerator, denominator pair + """ + @classmethod + def _parse_value(cls, stream_rdr, offset, value_count, value_offset): + """ + Return the rational (numerator / denominator) value at *value_offset* + in *stream_rdr* as a floating-point number. Only supports single + values at present. + """ + if value_count == 1: + numerator = stream_rdr.read_long(value_offset) + denominator = stream_rdr.read_long(value_offset, 4) + return numerator / denominator + else: # pragma: no cover + return 'Multi-value Rational NOT IMPLEMENTED' diff --git a/build/lib/docx/opc/__init__.py b/build/lib/docx/opc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/lib/docx/opc/compat.py b/build/lib/docx/opc/compat.py new file mode 100644 index 000000000..d944fe43b --- /dev/null +++ b/build/lib/docx/opc/compat.py @@ -0,0 +1,50 @@ +# encoding: utf-8 + +""" +Provides Python 2/3 compatibility objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import sys + +# =========================================================================== +# Python 3 versions +# =========================================================================== + +if sys.version_info >= (3, 0): + + def cls_method_fn(cls, method_name): + """ + Return the function object associated with the method of *cls* having + *method_name*. + """ + return getattr(cls, method_name) + + def is_string(obj): + """ + Return True if *obj* is a string, False otherwise. + """ + return isinstance(obj, str) + +# =========================================================================== +# Python 2 versions +# =========================================================================== + +else: + + def cls_method_fn(cls, method_name): + """ + Return the function object associated with the method of *cls* having + *method_name*. + """ + unbound_method = getattr(cls, method_name) + return unbound_method.__func__ + + def is_string(obj): + """ + Return True if *obj* is a string, False otherwise. + """ + return isinstance(obj, basestring) diff --git a/build/lib/docx/opc/constants.py b/build/lib/docx/opc/constants.py new file mode 100644 index 000000000..b90aa394a --- /dev/null +++ b/build/lib/docx/opc/constants.py @@ -0,0 +1,658 @@ +# encoding: utf-8 + +""" +Constant values related to the Open Packaging Convention, in particular, +content types and relationship types. +""" + + +class CONTENT_TYPE(object): + """ + Content type URIs (like MIME-types) that specify a part's format + """ + BMP = ( + 'image/bmp' + ) + DML_CHART = ( + 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml' + ) + DML_CHARTSHAPES = ( + 'application/vnd.openxmlformats-officedocument.drawingml.chartshapes' + '+xml' + ) + DML_DIAGRAM_COLORS = ( + 'application/vnd.openxmlformats-officedocument.drawingml.diagramColo' + 'rs+xml' + ) + DML_DIAGRAM_DATA = ( + 'application/vnd.openxmlformats-officedocument.drawingml.diagramData' + '+xml' + ) + DML_DIAGRAM_LAYOUT = ( + 'application/vnd.openxmlformats-officedocument.drawingml.diagramLayo' + 'ut+xml' + ) + DML_DIAGRAM_STYLE = ( + 'application/vnd.openxmlformats-officedocument.drawingml.diagramStyl' + 'e+xml' + ) + GIF = ( + 'image/gif' + ) + JPEG = ( + 'image/jpeg' + ) + MS_PHOTO = ( + 'image/vnd.ms-photo' + ) + OFC_CUSTOM_PROPERTIES = ( + 'application/vnd.openxmlformats-officedocument.custom-properties+xml' + ) + OFC_CUSTOM_XML_PROPERTIES = ( + 'application/vnd.openxmlformats-officedocument.customXmlProperties+x' + 'ml' + ) + OFC_DRAWING = ( + 'application/vnd.openxmlformats-officedocument.drawing+xml' + ) + OFC_EXTENDED_PROPERTIES = ( + 'application/vnd.openxmlformats-officedocument.extended-properties+x' + 'ml' + ) + OFC_OLE_OBJECT = ( + 'application/vnd.openxmlformats-officedocument.oleObject' + ) + OFC_PACKAGE = ( + 'application/vnd.openxmlformats-officedocument.package' + ) + OFC_THEME = ( + 'application/vnd.openxmlformats-officedocument.theme+xml' + ) + OFC_THEME_OVERRIDE = ( + 'application/vnd.openxmlformats-officedocument.themeOverride+xml' + ) + OFC_VML_DRAWING = ( + 'application/vnd.openxmlformats-officedocument.vmlDrawing' + ) + OPC_CORE_PROPERTIES = ( + 'application/vnd.openxmlformats-package.core-properties+xml' + ) + OPC_DIGITAL_SIGNATURE_CERTIFICATE = ( + 'application/vnd.openxmlformats-package.digital-signature-certificat' + 'e' + ) + OPC_DIGITAL_SIGNATURE_ORIGIN = ( + 'application/vnd.openxmlformats-package.digital-signature-origin' + ) + OPC_DIGITAL_SIGNATURE_XMLSIGNATURE = ( + 'application/vnd.openxmlformats-package.digital-signature-xmlsignatu' + 're+xml' + ) + OPC_RELATIONSHIPS = ( + 'application/vnd.openxmlformats-package.relationships+xml' + ) + PML_COMMENTS = ( + 'application/vnd.openxmlformats-officedocument.presentationml.commen' + 'ts+xml' + ) + PML_COMMENT_AUTHORS = ( + 'application/vnd.openxmlformats-officedocument.presentationml.commen' + 'tAuthors+xml' + ) + PML_HANDOUT_MASTER = ( + 'application/vnd.openxmlformats-officedocument.presentationml.handou' + 'tMaster+xml' + ) + PML_NOTES_MASTER = ( + 'application/vnd.openxmlformats-officedocument.presentationml.notesM' + 'aster+xml' + ) + PML_NOTES_SLIDE = ( + 'application/vnd.openxmlformats-officedocument.presentationml.notesS' + 'lide+xml' + ) + PML_PRESENTATION_MAIN = ( + 'application/vnd.openxmlformats-officedocument.presentationml.presen' + 'tation.main+xml' + ) + PML_PRES_PROPS = ( + 'application/vnd.openxmlformats-officedocument.presentationml.presPr' + 'ops+xml' + ) + PML_PRINTER_SETTINGS = ( + 'application/vnd.openxmlformats-officedocument.presentationml.printe' + 'rSettings' + ) + PML_SLIDE = ( + 'application/vnd.openxmlformats-officedocument.presentationml.slide+' + 'xml' + ) + PML_SLIDESHOW_MAIN = ( + 'application/vnd.openxmlformats-officedocument.presentationml.slides' + 'how.main+xml' + ) + PML_SLIDE_LAYOUT = ( + 'application/vnd.openxmlformats-officedocument.presentationml.slideL' + 'ayout+xml' + ) + PML_SLIDE_MASTER = ( + 'application/vnd.openxmlformats-officedocument.presentationml.slideM' + 'aster+xml' + ) + PML_SLIDE_UPDATE_INFO = ( + 'application/vnd.openxmlformats-officedocument.presentationml.slideU' + 'pdateInfo+xml' + ) + PML_TABLE_STYLES = ( + 'application/vnd.openxmlformats-officedocument.presentationml.tableS' + 'tyles+xml' + ) + PML_TAGS = ( + 'application/vnd.openxmlformats-officedocument.presentationml.tags+x' + 'ml' + ) + PML_TEMPLATE_MAIN = ( + 'application/vnd.openxmlformats-officedocument.presentationml.templa' + 'te.main+xml' + ) + PML_VIEW_PROPS = ( + 'application/vnd.openxmlformats-officedocument.presentationml.viewPr' + 'ops+xml' + ) + PNG = ( + 'image/png' + ) + SML_CALC_CHAIN = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.calcCha' + 'in+xml' + ) + SML_CHARTSHEET = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsh' + 'eet+xml' + ) + SML_COMMENTS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.comment' + 's+xml' + ) + SML_CONNECTIONS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.connect' + 'ions+xml' + ) + SML_CUSTOM_PROPERTY = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.customP' + 'roperty' + ) + SML_DIALOGSHEET = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.dialogs' + 'heet+xml' + ) + SML_EXTERNAL_LINK = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.externa' + 'lLink+xml' + ) + SML_PIVOT_CACHE_DEFINITION = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCa' + 'cheDefinition+xml' + ) + SML_PIVOT_CACHE_RECORDS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCa' + 'cheRecords+xml' + ) + SML_PIVOT_TABLE = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTa' + 'ble+xml' + ) + SML_PRINTER_SETTINGS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.printer' + 'Settings' + ) + SML_QUERY_TABLE = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.queryTa' + 'ble+xml' + ) + SML_REVISION_HEADERS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisio' + 'nHeaders+xml' + ) + SML_REVISION_LOG = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisio' + 'nLog+xml' + ) + SML_SHARED_STRINGS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedS' + 'trings+xml' + ) + SML_SHEET = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ) + SML_SHEET_MAIN = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.m' + 'ain+xml' + ) + SML_SHEET_METADATA = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMe' + 'tadata+xml' + ) + SML_STYLES = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+' + 'xml' + ) + SML_TABLE = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+x' + 'ml' + ) + SML_TABLE_SINGLE_CELLS = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.tableSi' + 'ngleCells+xml' + ) + SML_TEMPLATE_MAIN = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.templat' + 'e.main+xml' + ) + SML_USER_NAMES = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.userNam' + 'es+xml' + ) + SML_VOLATILE_DEPENDENCIES = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.volatil' + 'eDependencies+xml' + ) + SML_WORKSHEET = ( + 'application/vnd.openxmlformats-officedocument.spreadsheetml.workshe' + 'et+xml' + ) + TIFF = ( + 'image/tiff' + ) + WML_COMMENTS = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.comm' + 'ents+xml' + ) + WML_DOCUMENT = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.docu' + 'ment' + ) + WML_DOCUMENT_GLOSSARY = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.docu' + 'ment.glossary+xml' + ) + WML_DOCUMENT_MAIN = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.docu' + 'ment.main+xml' + ) + WML_ENDNOTES = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.endn' + 'otes+xml' + ) + WML_FONT_TABLE = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.font' + 'Table+xml' + ) + WML_FOOTER = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.foot' + 'er+xml' + ) + WML_FOOTNOTES = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.foot' + 'notes+xml' + ) + WML_HEADER = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.head' + 'er+xml' + ) + WML_NUMBERING = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.numb' + 'ering+xml' + ) + WML_PRINTER_SETTINGS = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.prin' + 'terSettings' + ) + WML_SETTINGS = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.sett' + 'ings+xml' + ) + WML_STYLES = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.styl' + 'es+xml' + ) + WML_WEB_SETTINGS = ( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.webS' + 'ettings+xml' + ) + XML = ( + 'application/xml' + ) + X_EMF = ( + 'image/x-emf' + ) + X_FONTDATA = ( + 'application/x-fontdata' + ) + X_FONT_TTF = ( + 'application/x-font-ttf' + ) + X_WMF = ( + 'image/x-wmf' + ) + + +class NAMESPACE(object): + """Constant values for OPC XML namespaces""" + DML_WORDPROCESSING_DRAWING = ( + 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDraw' + 'ing' + ) + OFC_RELATIONSHIPS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + ) + OPC_RELATIONSHIPS = ( + 'http://schemas.openxmlformats.org/package/2006/relationships' + ) + OPC_CONTENT_TYPES = ( + 'http://schemas.openxmlformats.org/package/2006/content-types' + ) + WML_MAIN = ( + 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' + ) + + +class RELATIONSHIP_TARGET_MODE(object): + """Open XML relationship target modes""" + EXTERNAL = 'External' + INTERNAL = 'Internal' + + +class RELATIONSHIP_TYPE(object): + AUDIO = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/audio' + ) + A_F_CHUNK = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/aFChunk' + ) + CALC_CHAIN = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/calcChain' + ) + CERTIFICATE = ( + 'http://schemas.openxmlformats.org/package/2006/relationships/digita' + 'l-signature/certificate' + ) + CHART = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/chart' + ) + CHARTSHEET = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/chartsheet' + ) + CHART_USER_SHAPES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/chartUserShapes' + ) + COMMENTS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/comments' + ) + COMMENT_AUTHORS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/commentAuthors' + ) + CONNECTIONS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/connections' + ) + CONTROL = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/control' + ) + CORE_PROPERTIES = ( + 'http://schemas.openxmlformats.org/package/2006/relationships/metada' + 'ta/core-properties' + ) + CUSTOM_PROPERTIES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/custom-properties' + ) + CUSTOM_PROPERTY = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/customProperty' + ) + CUSTOM_XML = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/customXml' + ) + CUSTOM_XML_PROPS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/customXmlProps' + ) + DIAGRAM_COLORS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/diagramColors' + ) + DIAGRAM_DATA = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/diagramData' + ) + DIAGRAM_LAYOUT = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/diagramLayout' + ) + DIAGRAM_QUICK_STYLE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/diagramQuickStyle' + ) + DIALOGSHEET = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/dialogsheet' + ) + DRAWING = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/drawing' + ) + ENDNOTES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/endnotes' + ) + EXTENDED_PROPERTIES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/extended-properties' + ) + EXTERNAL_LINK = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/externalLink' + ) + FONT = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/font' + ) + FONT_TABLE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/fontTable' + ) + FOOTER = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/footer' + ) + FOOTNOTES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/footnotes' + ) + GLOSSARY_DOCUMENT = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/glossaryDocument' + ) + HANDOUT_MASTER = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/handoutMaster' + ) + HEADER = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/header' + ) + HYPERLINK = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/hyperlink' + ) + IMAGE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/image' + ) + NOTES_MASTER = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/notesMaster' + ) + NOTES_SLIDE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/notesSlide' + ) + NUMBERING = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/numbering' + ) + OFFICE_DOCUMENT = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/officeDocument' + ) + OLE_OBJECT = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/oleObject' + ) + ORIGIN = ( + 'http://schemas.openxmlformats.org/package/2006/relationships/digita' + 'l-signature/origin' + ) + PACKAGE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/package' + ) + PIVOT_CACHE_DEFINITION = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/pivotCacheDefinition' + ) + PIVOT_CACHE_RECORDS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/spreadsheetml/pivotCacheRecords' + ) + PIVOT_TABLE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/pivotTable' + ) + PRES_PROPS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/presProps' + ) + PRINTER_SETTINGS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/printerSettings' + ) + QUERY_TABLE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/queryTable' + ) + REVISION_HEADERS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/revisionHeaders' + ) + REVISION_LOG = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/revisionLog' + ) + SETTINGS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/settings' + ) + SHARED_STRINGS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/sharedStrings' + ) + SHEET_METADATA = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/sheetMetadata' + ) + SIGNATURE = ( + 'http://schemas.openxmlformats.org/package/2006/relationships/digita' + 'l-signature/signature' + ) + SLIDE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/slide' + ) + SLIDE_LAYOUT = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/slideLayout' + ) + SLIDE_MASTER = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/slideMaster' + ) + SLIDE_UPDATE_INFO = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/slideUpdateInfo' + ) + STYLES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/styles' + ) + TABLE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/table' + ) + TABLE_SINGLE_CELLS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/tableSingleCells' + ) + TABLE_STYLES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/tableStyles' + ) + TAGS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/tags' + ) + THEME = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/theme' + ) + THEME_OVERRIDE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/themeOverride' + ) + THUMBNAIL = ( + 'http://schemas.openxmlformats.org/package/2006/relationships/metada' + 'ta/thumbnail' + ) + USERNAMES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/usernames' + ) + VIDEO = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/video' + ) + VIEW_PROPS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/viewProps' + ) + VML_DRAWING = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/vmlDrawing' + ) + VOLATILE_DEPENDENCIES = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/volatileDependencies' + ) + WEB_SETTINGS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/webSettings' + ) + WORKSHEET_SOURCE = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/worksheetSource' + ) + XML_MAPS = ( + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' + '/xmlMaps' + ) diff --git a/build/lib/docx/opc/coreprops.py b/build/lib/docx/opc/coreprops.py new file mode 100644 index 000000000..2d38dabd3 --- /dev/null +++ b/build/lib/docx/opc/coreprops.py @@ -0,0 +1,139 @@ +# encoding: utf-8 + +""" +The :mod:`pptx.packaging` module coheres around the concerns of reading and +writing presentations to and from a .pptx file. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + + +class CoreProperties(object): + """ + Corresponds to part named ``/docProps/core.xml``, containing the core + document properties for this document package. + """ + def __init__(self, element): + self._element = element + + @property + def author(self): + return self._element.author_text + + @author.setter + def author(self, value): + self._element.author_text = value + + @property + def category(self): + return self._element.category_text + + @category.setter + def category(self, value): + self._element.category_text = value + + @property + def comments(self): + return self._element.comments_text + + @comments.setter + def comments(self, value): + self._element.comments_text = value + + @property + def content_status(self): + return self._element.contentStatus_text + + @content_status.setter + def content_status(self, value): + self._element.contentStatus_text = value + + @property + def created(self): + return self._element.created_datetime + + @created.setter + def created(self, value): + self._element.created_datetime = value + + @property + def identifier(self): + return self._element.identifier_text + + @identifier.setter + def identifier(self, value): + self._element.identifier_text = value + + @property + def keywords(self): + return self._element.keywords_text + + @keywords.setter + def keywords(self, value): + self._element.keywords_text = value + + @property + def language(self): + return self._element.language_text + + @language.setter + def language(self, value): + self._element.language_text = value + + @property + def last_modified_by(self): + return self._element.lastModifiedBy_text + + @last_modified_by.setter + def last_modified_by(self, value): + self._element.lastModifiedBy_text = value + + @property + def last_printed(self): + return self._element.lastPrinted_datetime + + @last_printed.setter + def last_printed(self, value): + self._element.lastPrinted_datetime = value + + @property + def modified(self): + return self._element.modified_datetime + + @modified.setter + def modified(self, value): + self._element.modified_datetime = value + + @property + def revision(self): + return self._element.revision_number + + @revision.setter + def revision(self, value): + self._element.revision_number = value + + @property + def subject(self): + return self._element.subject_text + + @subject.setter + def subject(self, value): + self._element.subject_text = value + + @property + def title(self): + return self._element.title_text + + @title.setter + def title(self, value): + self._element.title_text = value + + @property + def version(self): + return self._element.version_text + + @version.setter + def version(self, value): + self._element.version_text = value diff --git a/build/lib/docx/opc/exceptions.py b/build/lib/docx/opc/exceptions.py new file mode 100644 index 000000000..b8e6de43f --- /dev/null +++ b/build/lib/docx/opc/exceptions.py @@ -0,0 +1,19 @@ +# encoding: utf-8 + +""" +Exceptions specific to python-opc + +The base exception class is OpcError. +""" + + +class OpcError(Exception): + """ + Base error class for python-opc + """ + + +class PackageNotFoundError(OpcError): + """ + Raised when a package cannot be found at the specified path. + """ diff --git a/build/lib/docx/opc/oxml.py b/build/lib/docx/opc/oxml.py new file mode 100644 index 000000000..494b31dca --- /dev/null +++ b/build/lib/docx/opc/oxml.py @@ -0,0 +1,292 @@ +# encoding: utf-8 + +""" +Temporary stand-in for main oxml module that came across with the +PackageReader transplant. Probably much will get replaced with objects from +the pptx.oxml.core and then this module will either get deleted or only hold +the package related custom element classes. +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from lxml import etree + +from .constants import NAMESPACE as NS, RELATIONSHIP_TARGET_MODE as RTM + + +# configure XML parser +element_class_lookup = etree.ElementNamespaceClassLookup() +oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) +oxml_parser.set_element_class_lookup(element_class_lookup) + +nsmap = { + 'ct': NS.OPC_CONTENT_TYPES, + 'pr': NS.OPC_RELATIONSHIPS, + 'r': NS.OFC_RELATIONSHIPS, +} + + +# =========================================================================== +# functions +# =========================================================================== + +def parse_xml(text): + """ + ``etree.fromstring()`` replacement that uses oxml parser + """ + return etree.fromstring(text, oxml_parser) + + +def qn(tag): + """ + Stands for "qualified name", a utility function to turn a namespace + prefixed tag name into a Clark-notation qualified tag name for lxml. For + example, ``qn('p:cSld')`` returns ``'{http://schemas.../main}cSld'``. + """ + prefix, tagroot = tag.split(':') + uri = nsmap[prefix] + return '{%s}%s' % (uri, tagroot) + + +def serialize_part_xml(part_elm): + """ + Serialize *part_elm* etree element to XML suitable for storage as an XML + part. That is to say, no insignificant whitespace added for readability, + and an appropriate XML declaration added with UTF-8 encoding specified. + """ + return etree.tostring(part_elm, encoding='UTF-8', standalone=True) + + +def serialize_for_reading(element): + """ + Serialize *element* to human-readable XML suitable for tests. No XML + declaration. + """ + return etree.tostring(element, encoding='unicode', pretty_print=True) + + +# =========================================================================== +# Custom element classes +# =========================================================================== + +class BaseOxmlElement(etree.ElementBase): + """ + Base class for all custom element classes, to add standardized behavior + to all classes in one place. + """ + @property + def xml(self): + """ + Return XML string for this element, suitable for testing purposes. + Pretty printed for readability and without an XML declaration at the + top. + """ + return serialize_for_reading(self) + + +class CT_Default(BaseOxmlElement): + """ + ```` element, specifying the default content type to be applied + to a part with the specified extension. + """ + @property + def content_type(self): + """ + String held in the ``ContentType`` attribute of this ```` + element. + """ + return self.get('ContentType') + + @property + def extension(self): + """ + String held in the ``Extension`` attribute of this ```` + element. + """ + return self.get('Extension') + + @staticmethod + def new(ext, content_type): + """ + Return a new ```` element with attributes set to parameter + values. + """ + xml = '' % nsmap['ct'] + default = parse_xml(xml) + default.set('Extension', ext) + default.set('ContentType', content_type) + return default + + +class CT_Override(BaseOxmlElement): + """ + ```` element, specifying the content type to be applied for a + part with the specified partname. + """ + @property + def content_type(self): + """ + String held in the ``ContentType`` attribute of this ```` + element. + """ + return self.get('ContentType') + + @staticmethod + def new(partname, content_type): + """ + Return a new ```` element with attributes set to parameter + values. + """ + xml = '' % nsmap['ct'] + override = parse_xml(xml) + override.set('PartName', partname) + override.set('ContentType', content_type) + return override + + @property + def partname(self): + """ + String held in the ``PartName`` attribute of this ```` + element. + """ + return self.get('PartName') + + +class CT_Relationship(BaseOxmlElement): + """ + ```` element, representing a single relationship from a + source to a target part. + """ + @staticmethod + def new(rId, reltype, target, target_mode=RTM.INTERNAL): + """ + Return a new ```` element. + """ + xml = '' % nsmap['pr'] + relationship = parse_xml(xml) + relationship.set('Id', rId) + relationship.set('Type', reltype) + relationship.set('Target', target) + if target_mode == RTM.EXTERNAL: + relationship.set('TargetMode', RTM.EXTERNAL) + return relationship + + @property + def rId(self): + """ + String held in the ``Id`` attribute of this ```` + element. + """ + return self.get('Id') + + @property + def reltype(self): + """ + String held in the ``Type`` attribute of this ```` + element. + """ + return self.get('Type') + + @property + def target_ref(self): + """ + String held in the ``Target`` attribute of this ```` + element. + """ + return self.get('Target') + + @property + def target_mode(self): + """ + String held in the ``TargetMode`` attribute of this + ```` element, either ``Internal`` or ``External``. + Defaults to ``Internal``. + """ + return self.get('TargetMode', RTM.INTERNAL) + + +class CT_Relationships(BaseOxmlElement): + """ + ```` element, the root element in a .rels file. + """ + def add_rel(self, rId, reltype, target, is_external=False): + """ + Add a child ```` element with attributes set according + to parameter values. + """ + target_mode = RTM.EXTERNAL if is_external else RTM.INTERNAL + relationship = CT_Relationship.new(rId, reltype, target, target_mode) + self.append(relationship) + + @staticmethod + def new(): + """ + Return a new ```` element. + """ + xml = '' % nsmap['pr'] + relationships = parse_xml(xml) + return relationships + + @property + def Relationship_lst(self): + """ + Return a list containing all the ```` child elements. + """ + return self.findall(qn('pr:Relationship')) + + @property + def xml(self): + """ + Return XML string for this element, suitable for saving in a .rels + stream, not pretty printed and with an XML declaration at the top. + """ + return serialize_part_xml(self) + + +class CT_Types(BaseOxmlElement): + """ + ```` element, the container element for Default and Override + elements in [Content_Types].xml. + """ + def add_default(self, ext, content_type): + """ + Add a child ```` element with attributes set to parameter + values. + """ + default = CT_Default.new(ext, content_type) + self.append(default) + + def add_override(self, partname, content_type): + """ + Add a child ```` element with attributes set to parameter + values. + """ + override = CT_Override.new(partname, content_type) + self.append(override) + + @property + def defaults(self): + return self.findall(qn('ct:Default')) + + @staticmethod + def new(): + """ + Return a new ```` element. + """ + xml = '' % nsmap['ct'] + types = parse_xml(xml) + return types + + @property + def overrides(self): + return self.findall(qn('ct:Override')) + + +ct_namespace = element_class_lookup.get_namespace(nsmap['ct']) +ct_namespace['Default'] = CT_Default +ct_namespace['Override'] = CT_Override +ct_namespace['Types'] = CT_Types + +pr_namespace = element_class_lookup.get_namespace(nsmap['pr']) +pr_namespace['Relationship'] = CT_Relationship +pr_namespace['Relationships'] = CT_Relationships diff --git a/build/lib/docx/opc/package.py b/build/lib/docx/opc/package.py new file mode 100644 index 000000000..5b817274f --- /dev/null +++ b/build/lib/docx/opc/package.py @@ -0,0 +1,235 @@ +# encoding: utf-8 + +""" +The :mod:`pptx.packaging` module coheres around the concerns of reading and +writing presentations to and from a .pptx file. +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from .constants import RELATIONSHIP_TYPE as RT +from .packuri import PACKAGE_URI +from .part import PartFactory +from .parts.coreprops import CorePropertiesPart +from docx.parts.comments import CommentsPart +from .pkgreader import PackageReader +from .pkgwriter import PackageWriter +from .rel import Relationships +from .shared import lazyproperty + + +class OpcPackage(object): + """ + Main API class for |python-opc|. A new instance is constructed by calling + the :meth:`open` class method with a path to a package file or file-like + object containing one. + """ + def __init__(self): + super(OpcPackage, self).__init__() + + def after_unmarshal(self): + """ + Entry point for any post-unmarshaling processing. May be overridden + by subclasses without forwarding call to super. + """ + # don't place any code here, just catch call if not overridden by + # subclass + pass + + @property + def core_properties(self): + """ + |CoreProperties| object providing read/write access to the Dublin + Core properties for this document. + """ + return self._core_properties_part.core_properties + + def iter_rels(self): + """ + Generate exactly one reference to each relationship in the package by + performing a depth-first traversal of the rels graph. + """ + def walk_rels(source, visited=None): + visited = [] if visited is None else visited + for rel in source.rels.values(): + yield rel + if rel.is_external: + continue + part = rel.target_part + if part in visited: + continue + visited.append(part) + new_source = part + for rel in walk_rels(new_source, visited): + yield rel + + for rel in walk_rels(self): + yield rel + + def iter_parts(self): + """ + Generate exactly one reference to each of the parts in the package by + performing a depth-first traversal of the rels graph. + """ + def walk_parts(source, visited=list()): + for rel in source.rels.values(): + if rel.is_external: + continue + part = rel.target_part + if part in visited: + continue + visited.append(part) + yield part + new_source = part + for part in walk_parts(new_source, visited): + yield part + + for part in walk_parts(self): + yield part + + def load_rel(self, reltype, target, rId, is_external=False): + """ + Return newly added |_Relationship| instance of *reltype* between this + part and *target* with key *rId*. Target mode is set to + ``RTM.EXTERNAL`` if *is_external* is |True|. Intended for use during + load from a serialized package, where the rId is well known. Other + methods exist for adding a new relationship to the package during + processing. + """ + return self.rels.add_relationship(reltype, target, rId, is_external) + + @property + def main_document_part(self): + """ + Return a reference to the main document part for this package. + Examples include a document part for a WordprocessingML package, a + presentation part for a PresentationML package, or a workbook part + for a SpreadsheetML package. + """ + return self.part_related_by(RT.OFFICE_DOCUMENT) + + @classmethod + def open(cls, pkg_file): + """ + Return an |OpcPackage| instance loaded with the contents of + *pkg_file*. + """ + pkg_reader = PackageReader.from_file(pkg_file) + package = cls() + Unmarshaller.unmarshal(pkg_reader, package, PartFactory) + return package + + def part_related_by(self, reltype): + """ + Return part to which this package has a relationship of *reltype*. + Raises |KeyError| if no such relationship is found and |ValueError| + if more than one such relationship is found. + """ + return self.rels.part_with_reltype(reltype) + + @property + def parts(self): + """ + Return a list containing a reference to each of the parts in this + package. + """ + return [part for part in self.iter_parts()] + + def relate_to(self, part, reltype): + """ + Return rId key of relationship to *part*, from the existing + relationship if there is one, otherwise a newly created one. + """ + rel = self.rels.get_or_add(reltype, part) + return rel.rId + + @lazyproperty + def rels(self): + """ + Return a reference to the |Relationships| instance holding the + collection of relationships for this package. + """ + return Relationships(PACKAGE_URI.baseURI) + + def save(self, pkg_file): + """ + Save this package to *pkg_file*, where *file* can be either a path to + a file (a string) or a file-like object. + """ + for part in self.parts: + part.before_marshal() + PackageWriter.write(pkg_file, self.rels, self.parts) + + @property + def _core_properties_part(self): + """ + |CorePropertiesPart| object related to this package. Creates + a default core properties part if one is not present (not common). + """ + try: + return self.part_related_by(RT.CORE_PROPERTIES) + except KeyError: + core_properties_part = CorePropertiesPart.default(self) + self.relate_to(core_properties_part, RT.CORE_PROPERTIES) + return core_properties_part + + @property + def _comments_part(self): + """ + |CommentsPart| object related to this package. Creates + a default Comments part if one is not present. + """ + try: + return self.part_related_by(RT.COMMENTS) + except KeyError: + comments_part = CommentsPart.default(self) + self.relate_to(comments_part, RT.COMMENTS) + return comments_part + + +class Unmarshaller(object): + """ + Hosts static methods for unmarshalling a package from a |PackageReader| + instance. + """ + @staticmethod + def unmarshal(pkg_reader, package, part_factory): + """ + Construct graph of parts and realized relationships based on the + contents of *pkg_reader*, delegating construction of each part to + *part_factory*. Package relationships are added to *pkg*. + """ + parts = Unmarshaller._unmarshal_parts( + pkg_reader, package, part_factory + ) + Unmarshaller._unmarshal_relationships(pkg_reader, package, parts) + for part in parts.values(): + part.after_unmarshal() + package.after_unmarshal() + + @staticmethod + def _unmarshal_parts(pkg_reader, package, part_factory): + """ + Return a dictionary of |Part| instances unmarshalled from + *pkg_reader*, keyed by partname. Side-effect is that each part in + *pkg_reader* is constructed using *part_factory*. + """ + parts = {} + for partname, content_type, reltype, blob in pkg_reader.iter_sparts(): + parts[partname] = part_factory( + partname, content_type, reltype, blob, package + ) + return parts + + @staticmethod + def _unmarshal_relationships(pkg_reader, package, parts): + """ + Add a relationship to the source object corresponding to each of the + relationships in *pkg_reader* with its target_part set to the actual + target part in *parts*. + """ + for source_uri, srel in pkg_reader.iter_srels(): + source = package if source_uri == '/' else parts[source_uri] + target = (srel.target_ref if srel.is_external + else parts[srel.target_partname]) + source.load_rel(srel.reltype, target, srel.rId, srel.is_external) diff --git a/build/lib/docx/opc/packuri.py b/build/lib/docx/opc/packuri.py new file mode 100644 index 000000000..621ed92e5 --- /dev/null +++ b/build/lib/docx/opc/packuri.py @@ -0,0 +1,117 @@ +# encoding: utf-8 + +""" +Provides the PackURI value type along with some useful known pack URI strings +such as PACKAGE_URI. +""" + +import posixpath +import re + + +class PackURI(str): + """ + Provides access to pack URI components such as the baseURI and the + filename slice. Behaves as |str| otherwise. + """ + _filename_re = re.compile('([a-zA-Z]+)([1-9][0-9]*)?') + + def __new__(cls, pack_uri_str): + if not pack_uri_str[0] == '/': + tmpl = "PackURI must begin with slash, got '%s'" + raise ValueError(tmpl % pack_uri_str) + return str.__new__(cls, pack_uri_str) + + @staticmethod + def from_rel_ref(baseURI, relative_ref): + """ + Return a |PackURI| instance containing the absolute pack URI formed by + translating *relative_ref* onto *baseURI*. + """ + joined_uri = posixpath.join(baseURI, relative_ref) + abs_uri = posixpath.abspath(joined_uri) + return PackURI(abs_uri) + + @property + def baseURI(self): + """ + The base URI of this pack URI, the directory portion, roughly + speaking. E.g. ``'/ppt/slides'`` for ``'/ppt/slides/slide1.xml'``. + For the package pseudo-partname '/', baseURI is '/'. + """ + return posixpath.split(self)[0] + + @property + def ext(self): + """ + The extension portion of this pack URI, e.g. ``'xml'`` for + ``'/word/document.xml'``. Note the period is not included. + """ + # raw_ext is either empty string or starts with period, e.g. '.xml' + raw_ext = posixpath.splitext(self)[1] + return raw_ext[1:] if raw_ext.startswith('.') else raw_ext + + @property + def filename(self): + """ + The "filename" portion of this pack URI, e.g. ``'slide1.xml'`` for + ``'/ppt/slides/slide1.xml'``. For the package pseudo-partname '/', + filename is ''. + """ + return posixpath.split(self)[1] + + @property + def idx(self): + """ + Return partname index as integer for tuple partname or None for + singleton partname, e.g. ``21`` for ``'/ppt/slides/slide21.xml'`` and + |None| for ``'/ppt/presentation.xml'``. + """ + filename = self.filename + if not filename: + return None + name_part = posixpath.splitext(filename)[0] # filename w/ext removed + match = self._filename_re.match(name_part) + if match is None: + return None + if match.group(2): + return int(match.group(2)) + return None + + @property + def membername(self): + """ + The pack URI with the leading slash stripped off, the form used as + the Zip file membername for the package item. Returns '' for the + package pseudo-partname '/'. + """ + return self[1:] + + def relative_ref(self, baseURI): + """ + Return string containing relative reference to package item from + *baseURI*. E.g. PackURI('/ppt/slideLayouts/slideLayout1.xml') would + return '../slideLayouts/slideLayout1.xml' for baseURI '/ppt/slides'. + """ + # workaround for posixpath bug in 2.6, doesn't generate correct + # relative path when *start* (second) parameter is root ('/') + if baseURI == '/': + relpath = self[1:] + else: + relpath = posixpath.relpath(self, baseURI) + return relpath + + @property + def rels_uri(self): + """ + The pack URI of the .rels part corresponding to the current pack URI. + Only produces sensible output if the pack URI is a partname or the + package pseudo-partname '/'. + """ + rels_filename = '%s.rels' % self.filename + rels_uri_str = posixpath.join(self.baseURI, '_rels', rels_filename) + return PackURI(rels_uri_str) + + +PACKAGE_URI = PackURI('/') +CONTENT_TYPES_URI = PackURI('/[Content_Types].xml') diff --git a/build/lib/docx/opc/part.py b/build/lib/docx/opc/part.py new file mode 100644 index 000000000..928d3c183 --- /dev/null +++ b/build/lib/docx/opc/part.py @@ -0,0 +1,241 @@ +# encoding: utf-8 + +""" +Open Packaging Convention (OPC) objects related to package parts. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .compat import cls_method_fn +from .oxml import serialize_part_xml +from ..oxml import parse_xml +from .packuri import PackURI +from .rel import Relationships +from .shared import lazyproperty + + +class Part(object): + """ + Base class for package parts. Provides common properties and methods, but + intended to be subclassed in client code to implement specific part + behaviors. + """ + def __init__(self, partname, content_type, blob=None, package=None): + super(Part, self).__init__() + self._partname = partname + self._content_type = content_type + self._blob = blob + self._package = package + + def after_unmarshal(self): + """ + Entry point for post-unmarshaling processing, for example to parse + the part XML. May be overridden by subclasses without forwarding call + to super. + """ + # don't place any code here, just catch call if not overridden by + # subclass + pass + + def before_marshal(self): + """ + Entry point for pre-serialization processing, for example to finalize + part naming if necessary. May be overridden by subclasses without + forwarding call to super. + """ + # don't place any code here, just catch call if not overridden by + # subclass + pass + + @property + def blob(self): + """ + Contents of this package part as a sequence of bytes. May be text or + binary. Intended to be overridden by subclasses. Default behavior is + to return load blob. + """ + return self._blob + + @property + def content_type(self): + """ + Content type of this part. + """ + return self._content_type + + def drop_rel(self, rId): + """ + Remove the relationship identified by *rId* if its reference count + is less than 2. Relationships with a reference count of 0 are + implicit relationships. + """ + if self._rel_ref_count(rId) < 2: + del self.rels[rId] + + @classmethod + def load(cls, partname, content_type, blob, package): + return cls(partname, content_type, blob, package) + + def load_rel(self, reltype, target, rId, is_external=False): + """ + Return newly added |_Relationship| instance of *reltype* between this + part and *target* with key *rId*. Target mode is set to + ``RTM.EXTERNAL`` if *is_external* is |True|. Intended for use during + load from a serialized package, where the rId is well-known. Other + methods exist for adding a new relationship to a part when + manipulating a part. + """ + return self.rels.add_relationship(reltype, target, rId, is_external) + + @property + def package(self): + """ + |OpcPackage| instance this part belongs to. + """ + return self._package + + @property + def partname(self): + """ + |PackURI| instance holding partname of this part, e.g. + '/ppt/slides/slide1.xml' + """ + return self._partname + + @partname.setter + def partname(self, partname): + if not isinstance(partname, PackURI): + tmpl = "partname must be instance of PackURI, got '%s'" + raise TypeError(tmpl % type(partname).__name__) + self._partname = partname + + def part_related_by(self, reltype): + """ + Return part to which this part has a relationship of *reltype*. + Raises |KeyError| if no such relationship is found and |ValueError| + if more than one such relationship is found. Provides ability to + resolve implicitly related part, such as Slide -> SlideLayout. + """ + return self.rels.part_with_reltype(reltype) + + def relate_to(self, target, reltype, is_external=False): + """ + Return rId key of relationship of *reltype* to *target*, from an + existing relationship if there is one, otherwise a newly created one. + """ + if is_external: + return self.rels.get_or_add_ext_rel(reltype, target) + else: + rel = self.rels.get_or_add(reltype, target) + return rel.rId + + @property + def related_parts(self): + """ + Dictionary mapping related parts by rId, so child objects can resolve + explicit relationships present in the part XML, e.g. sldIdLst to a + specific |Slide| instance. + """ + return self.rels.related_parts + + @lazyproperty + def rels(self): + """ + |Relationships| instance holding the relationships for this part. + """ + return Relationships(self._partname.baseURI) + + def target_ref(self, rId): + """ + Return URL contained in target ref of relationship identified by + *rId*. + """ + rel = self.rels[rId] + return rel.target_ref + + def _rel_ref_count(self, rId): + """ + Return the count of references in this part's XML to the relationship + identified by *rId*. + """ + rIds = self._element.xpath('//@r:id') + return len([_rId for _rId in rIds if _rId == rId]) + + +class PartFactory(object): + """ + Provides a way for client code to specify a subclass of |Part| to be + constructed by |Unmarshaller| based on its content type and/or a custom + callable. Setting ``PartFactory.part_class_selector`` to a callable + object will cause that object to be called with the parameters + ``content_type, reltype``, once for each part in the package. If the + callable returns an object, it is used as the class for that part. If it + returns |None|, part class selection falls back to the content type map + defined in ``PartFactory.part_type_for``. If no class is returned from + either of these, the class contained in ``PartFactory.default_part_type`` + is used to construct the part, which is by default ``opc.package.Part``. + """ + part_class_selector = None + part_type_for = {} + default_part_type = Part + + def __new__(cls, partname, content_type, reltype, blob, package): + PartClass = None + if cls.part_class_selector is not None: + part_class_selector = cls_method_fn(cls, 'part_class_selector') + PartClass = part_class_selector(content_type, reltype) + if PartClass is None: + PartClass = cls._part_cls_for(content_type) + return PartClass.load(partname, content_type, blob, package) + + @classmethod + def _part_cls_for(cls, content_type): + """ + Return the custom part class registered for *content_type*, or the + default part class if no custom class is registered for + *content_type*. + """ + if content_type in cls.part_type_for: + return cls.part_type_for[content_type] + return cls.default_part_type + + +class XmlPart(Part): + """ + Base class for package parts containing an XML payload, which is most of + them. Provides additional methods to the |Part| base class that take care + of parsing and reserializing the XML payload and managing relationships + to other parts. + """ + def __init__(self, partname, content_type, element, package): + super(XmlPart, self).__init__( + partname, content_type, package=package + ) + self._element = element + + @property + def blob(self): + return serialize_part_xml(self._element) + + @property + def element(self): + """ + The root XML element of this XML part. + """ + return self._element + + @classmethod + def load(cls, partname, content_type, blob, package): + element = parse_xml(blob) + return cls(partname, content_type, element, package) + + @property + def part(self): + """ + Part of the parent protocol, "children" of the document will not know + the part that contains them so must ask their parent object. That + chain of delegation ends here for child objects. + """ + return self diff --git a/build/lib/docx/opc/parts/__init__.py b/build/lib/docx/opc/parts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/lib/docx/opc/parts/coreprops.py b/build/lib/docx/opc/parts/coreprops.py new file mode 100644 index 000000000..3c692fb99 --- /dev/null +++ b/build/lib/docx/opc/parts/coreprops.py @@ -0,0 +1,54 @@ +# encoding: utf-8 + +""" +Core properties part, corresponds to ``/docProps/core.xml`` part in package. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from datetime import datetime + +from ..constants import CONTENT_TYPE as CT +from ..coreprops import CoreProperties +from ...oxml.coreprops import CT_CoreProperties +from ..packuri import PackURI +from ..part import XmlPart + + +class CorePropertiesPart(XmlPart): + """ + Corresponds to part named ``/docProps/core.xml``, containing the core + document properties for this document package. + """ + @classmethod + def default(cls, package): + """ + Return a new |CorePropertiesPart| object initialized with default + values for its base properties. + """ + core_properties_part = cls._new(package) + core_properties = core_properties_part.core_properties + core_properties.title = 'Word Document' + core_properties.last_modified_by = 'python-docx' + core_properties.revision = 1 + core_properties.modified = datetime.utcnow() + return core_properties_part + + @property + def core_properties(self): + """ + A |CoreProperties| object providing read/write access to the core + properties contained in this core properties part. + """ + return CoreProperties(self.element) + + @classmethod + def _new(cls, package): + partname = PackURI('/docProps/core.xml') + content_type = CT.OPC_CORE_PROPERTIES + coreProperties = CT_CoreProperties.new() + return CorePropertiesPart( + partname, content_type, coreProperties, package + ) diff --git a/build/lib/docx/opc/phys_pkg.py b/build/lib/docx/opc/phys_pkg.py new file mode 100644 index 000000000..c86a51994 --- /dev/null +++ b/build/lib/docx/opc/phys_pkg.py @@ -0,0 +1,155 @@ +# encoding: utf-8 + +""" +Provides a general interface to a *physical* OPC package, such as a zip file. +""" + +from __future__ import absolute_import + +import os + +from zipfile import ZipFile, is_zipfile, ZIP_DEFLATED + +from .compat import is_string +from .exceptions import PackageNotFoundError +from .packuri import CONTENT_TYPES_URI + + +class PhysPkgReader(object): + """ + Factory for physical package reader objects. + """ + def __new__(cls, pkg_file): + # if *pkg_file* is a string, treat it as a path + if is_string(pkg_file): + if os.path.isdir(pkg_file): + reader_cls = _DirPkgReader + elif is_zipfile(pkg_file): + reader_cls = _ZipPkgReader + else: + raise PackageNotFoundError( + "Package not found at '%s'" % pkg_file + ) + else: # assume it's a stream and pass it to Zip reader to sort out + reader_cls = _ZipPkgReader + + return super(PhysPkgReader, cls).__new__(reader_cls) + + +class PhysPkgWriter(object): + """ + Factory for physical package writer objects. + """ + def __new__(cls, pkg_file): + return super(PhysPkgWriter, cls).__new__(_ZipPkgWriter) + + +class _DirPkgReader(PhysPkgReader): + """ + Implements |PhysPkgReader| interface for an OPC package extracted into a + directory. + """ + def __init__(self, path): + """ + *path* is the path to a directory containing an expanded package. + """ + super(_DirPkgReader, self).__init__() + self._path = os.path.abspath(path) + + def blob_for(self, pack_uri): + """ + Return contents of file corresponding to *pack_uri* in package + directory. + """ + path = os.path.join(self._path, pack_uri.membername) + with open(path, 'rb') as f: + blob = f.read() + return blob + + def close(self): + """ + Provides interface consistency with |ZipFileSystem|, but does + nothing, a directory file system doesn't need closing. + """ + pass + + @property + def content_types_xml(self): + """ + Return the `[Content_Types].xml` blob from the package. + """ + return self.blob_for(CONTENT_TYPES_URI) + + def rels_xml_for(self, source_uri): + """ + Return rels item XML for source with *source_uri*, or None if the + item has no rels item. + """ + try: + rels_xml = self.blob_for(source_uri.rels_uri) + except IOError: + rels_xml = None + return rels_xml + + +class _ZipPkgReader(PhysPkgReader): + """ + Implements |PhysPkgReader| interface for a zip file OPC package. + """ + def __init__(self, pkg_file): + super(_ZipPkgReader, self).__init__() + self._zipf = ZipFile(pkg_file, 'r') + + def blob_for(self, pack_uri): + """ + Return blob corresponding to *pack_uri*. Raises |ValueError| if no + matching member is present in zip archive. + """ + return self._zipf.read(pack_uri.membername) + + def close(self): + """ + Close the zip archive, releasing any resources it is using. + """ + self._zipf.close() + + @property + def content_types_xml(self): + """ + Return the `[Content_Types].xml` blob from the zip package. + """ + return self.blob_for(CONTENT_TYPES_URI) + + def rels_xml_for(self, source_uri): + """ + Return rels item XML for source with *source_uri* or None if no rels + item is present. + """ + try: + rels_xml = self.blob_for(source_uri.rels_uri) + except KeyError: + rels_xml = None + return rels_xml + + +class _ZipPkgWriter(PhysPkgWriter): + """ + Implements |PhysPkgWriter| interface for a zip file OPC package. + """ + def __init__(self, pkg_file): + super(_ZipPkgWriter, self).__init__() + self._zipf = ZipFile(pkg_file, 'w', compression=ZIP_DEFLATED) + + def close(self): + """ + Close the zip archive, flushing any pending physical writes and + releasing any resources it's using. + """ + self._zipf.close() + + def write(self, pack_uri, blob): + """ + Write *blob* to this zip package with the membername corresponding to + *pack_uri*. + """ + self._zipf.writestr(pack_uri.membername, blob) diff --git a/build/lib/docx/opc/pkgreader.py b/build/lib/docx/opc/pkgreader.py new file mode 100644 index 000000000..ae80b3586 --- /dev/null +++ b/build/lib/docx/opc/pkgreader.py @@ -0,0 +1,298 @@ +# encoding: utf-8 + +""" +Provides a low-level, read-only API to a serialized Open Packaging Convention +(OPC) package. +""" + +from __future__ import absolute_import + +from .constants import RELATIONSHIP_TARGET_MODE as RTM +from .oxml import parse_xml +from .packuri import PACKAGE_URI, PackURI +from .phys_pkg import PhysPkgReader +from .shared import CaseInsensitiveDict + + +class PackageReader(object): + """ + Provides access to the contents of a zip-format OPC package via its + :attr:`serialized_parts` and :attr:`pkg_srels` attributes. + """ + def __init__(self, content_types, pkg_srels, sparts): + super(PackageReader, self).__init__() + self._pkg_srels = pkg_srels + self._sparts = sparts + + @staticmethod + def from_file(pkg_file): + """ + Return a |PackageReader| instance loaded with contents of *pkg_file*. + """ + phys_reader = PhysPkgReader(pkg_file) + content_types = _ContentTypeMap.from_xml(phys_reader.content_types_xml) + pkg_srels = PackageReader._srels_for(phys_reader, PACKAGE_URI) + sparts = PackageReader._load_serialized_parts( + phys_reader, pkg_srels, content_types + ) + phys_reader.close() + return PackageReader(content_types, pkg_srels, sparts) + + def iter_sparts(self): + """ + Generate a 4-tuple `(partname, content_type, reltype, blob)` for each + of the serialized parts in the package. + """ + for s in self._sparts: + yield (s.partname, s.content_type, s.reltype, s.blob) + + def iter_srels(self): + """ + Generate a 2-tuple `(source_uri, srel)` for each of the relationships + in the package. + """ + for srel in self._pkg_srels: + yield (PACKAGE_URI, srel) + for spart in self._sparts: + for srel in spart.srels: + yield (spart.partname, srel) + + @staticmethod + def _load_serialized_parts(phys_reader, pkg_srels, content_types): + """ + Return a list of |_SerializedPart| instances corresponding to the + parts in *phys_reader* accessible by walking the relationship graph + starting with *pkg_srels*. + """ + sparts = [] + part_walker = PackageReader._walk_phys_parts(phys_reader, pkg_srels) + for partname, blob, reltype, srels in part_walker: + content_type = content_types[partname] + spart = _SerializedPart( + partname, content_type, reltype, blob, srels + ) + sparts.append(spart) + return tuple(sparts) + + @staticmethod + def _srels_for(phys_reader, source_uri): + """ + Return |_SerializedRelationships| instance populated with + relationships for source identified by *source_uri*. + """ + rels_xml = phys_reader.rels_xml_for(source_uri) + return _SerializedRelationships.load_from_xml( + source_uri.baseURI, rels_xml) + + @staticmethod + def _walk_phys_parts(phys_reader, srels, visited_partnames=None): + """ + Generate a 4-tuple `(partname, blob, reltype, srels)` for each of the + parts in *phys_reader* by walking the relationship graph rooted at + srels. + """ + if visited_partnames is None: + visited_partnames = [] + for srel in srels: + if srel.is_external: + continue + partname = srel.target_partname + if partname in visited_partnames: + continue + visited_partnames.append(partname) + reltype = srel.reltype + part_srels = PackageReader._srels_for(phys_reader, partname) + blob = phys_reader.blob_for(partname) + yield (partname, blob, reltype, part_srels) + next_walker = PackageReader._walk_phys_parts( + phys_reader, part_srels, visited_partnames + ) + for partname, blob, reltype, srels in next_walker: + yield (partname, blob, reltype, srels) + + +class _ContentTypeMap(object): + """ + Value type providing dictionary semantics for looking up content type by + part name, e.g. ``content_type = cti['/ppt/presentation.xml']``. + """ + def __init__(self): + super(_ContentTypeMap, self).__init__() + self._overrides = CaseInsensitiveDict() + self._defaults = CaseInsensitiveDict() + + def __getitem__(self, partname): + """ + Return content type for part identified by *partname*. + """ + if not isinstance(partname, PackURI): + tmpl = "_ContentTypeMap key must be , got %s" + raise KeyError(tmpl % type(partname)) + if partname in self._overrides: + return self._overrides[partname] + if partname.ext in self._defaults: + return self._defaults[partname.ext] + tmpl = "no content type for partname '%s' in [Content_Types].xml" + raise KeyError(tmpl % partname) + + @staticmethod + def from_xml(content_types_xml): + """ + Return a new |_ContentTypeMap| instance populated with the contents + of *content_types_xml*. + """ + types_elm = parse_xml(content_types_xml) + ct_map = _ContentTypeMap() + for o in types_elm.overrides: + ct_map._add_override(o.partname, o.content_type) + for d in types_elm.defaults: + ct_map._add_default(d.extension, d.content_type) + return ct_map + + def _add_default(self, extension, content_type): + """ + Add the default mapping of *extension* to *content_type* to this + content type mapping. + """ + self._defaults[extension] = content_type + + def _add_override(self, partname, content_type): + """ + Add the default mapping of *partname* to *content_type* to this + content type mapping. + """ + self._overrides[partname] = content_type + + +class _SerializedPart(object): + """ + Value object for an OPC package part. Provides access to the partname, + content type, blob, and serialized relationships for the part. + """ + def __init__(self, partname, content_type, reltype, blob, srels): + super(_SerializedPart, self).__init__() + self._partname = partname + self._content_type = content_type + self._reltype = reltype + self._blob = blob + self._srels = srels + + @property + def partname(self): + return self._partname + + @property + def content_type(self): + return self._content_type + + @property + def blob(self): + return self._blob + + @property + def reltype(self): + """ + The referring relationship type of this part. + """ + return self._reltype + + @property + def srels(self): + return self._srels + + +class _SerializedRelationship(object): + """ + Value object representing a serialized relationship in an OPC package. + Serialized, in this case, means any target part is referred to via its + partname rather than a direct link to an in-memory |Part| object. + """ + def __init__(self, baseURI, rel_elm): + super(_SerializedRelationship, self).__init__() + self._baseURI = baseURI + self._rId = rel_elm.rId + self._reltype = rel_elm.reltype + self._target_mode = rel_elm.target_mode + self._target_ref = rel_elm.target_ref + + @property + def is_external(self): + """ + True if target_mode is ``RTM.EXTERNAL`` + """ + return self._target_mode == RTM.EXTERNAL + + @property + def reltype(self): + """Relationship type, like ``RT.OFFICE_DOCUMENT``""" + return self._reltype + + @property + def rId(self): + """ + Relationship id, like 'rId9', corresponds to the ``Id`` attribute on + the ``CT_Relationship`` element. + """ + return self._rId + + @property + def target_mode(self): + """ + String in ``TargetMode`` attribute of ``CT_Relationship`` element, + one of ``RTM.INTERNAL`` or ``RTM.EXTERNAL``. + """ + return self._target_mode + + @property + def target_ref(self): + """ + String in ``Target`` attribute of ``CT_Relationship`` element, a + relative part reference for internal target mode or an arbitrary URI, + e.g. an HTTP URL, for external target mode. + """ + return self._target_ref + + @property + def target_partname(self): + """ + |PackURI| instance containing partname targeted by this relationship. + Raises ``ValueError`` on reference if target_mode is ``'External'``. + Use :attr:`target_mode` to check before referencing. + """ + if self.is_external: + msg = ('target_partname attribute on Relationship is undefined w' + 'here TargetMode == "External"') + raise ValueError(msg) + # lazy-load _target_partname attribute + if not hasattr(self, '_target_partname'): + self._target_partname = PackURI.from_rel_ref(self._baseURI, + self.target_ref) + return self._target_partname + + +class _SerializedRelationships(object): + """ + Read-only sequence of |_SerializedRelationship| instances corresponding + to the relationships item XML passed to constructor. + """ + def __init__(self): + super(_SerializedRelationships, self).__init__() + self._srels = [] + + def __iter__(self): + """Support iteration, e.g. 'for x in srels:'""" + return self._srels.__iter__() + + @staticmethod + def load_from_xml(baseURI, rels_item_xml): + """ + Return |_SerializedRelationships| instance loaded with the + relationships contained in *rels_item_xml*. Returns an empty + collection if *rels_item_xml* is |None|. + """ + srels = _SerializedRelationships() + if rels_item_xml is not None: + rels_elm = parse_xml(rels_item_xml) + for rel_elm in rels_elm.Relationship_lst: + srels._srels.append(_SerializedRelationship(baseURI, rel_elm)) + return srels diff --git a/build/lib/docx/opc/pkgwriter.py b/build/lib/docx/opc/pkgwriter.py new file mode 100644 index 000000000..fccda6cd8 --- /dev/null +++ b/build/lib/docx/opc/pkgwriter.py @@ -0,0 +1,125 @@ +# encoding: utf-8 + +""" +Provides a low-level, write-only API to a serialized Open Packaging +Convention (OPC) package, essentially an implementation of OpcPackage.save() +""" + +from __future__ import absolute_import + +from .constants import CONTENT_TYPE as CT +from .oxml import CT_Types, serialize_part_xml +from .packuri import CONTENT_TYPES_URI, PACKAGE_URI +from .phys_pkg import PhysPkgWriter +from .shared import CaseInsensitiveDict +from .spec import default_content_types + + +class PackageWriter(object): + """ + Writes a zip-format OPC package to *pkg_file*, where *pkg_file* can be + either a path to a zip file (a string) or a file-like object. Its single + API method, :meth:`write`, is static, so this class is not intended to + be instantiated. + """ + @staticmethod + def write(pkg_file, pkg_rels, parts): + """ + Write a physical package (.pptx file) to *pkg_file* containing + *pkg_rels* and *parts* and a content types stream based on the + content types of the parts. + """ + phys_writer = PhysPkgWriter(pkg_file) + PackageWriter._write_content_types_stream(phys_writer, parts) + PackageWriter._write_pkg_rels(phys_writer, pkg_rels) + PackageWriter._write_parts(phys_writer, parts) + phys_writer.close() + + @staticmethod + def _write_content_types_stream(phys_writer, parts): + """ + Write ``[Content_Types].xml`` part to the physical package with an + appropriate content type lookup target for each part in *parts*. + """ + cti = _ContentTypesItem.from_parts(parts) + phys_writer.write(CONTENT_TYPES_URI, cti.blob) + + @staticmethod + def _write_parts(phys_writer, parts): + """ + Write the blob of each part in *parts* to the package, along with a + rels item for its relationships if and only if it has any. + """ + for part in parts: + phys_writer.write(part.partname, part.blob) + if len(part._rels): + phys_writer.write(part.partname.rels_uri, part._rels.xml) + + @staticmethod + def _write_pkg_rels(phys_writer, pkg_rels): + """ + Write the XML rels item for *pkg_rels* ('/_rels/.rels') to the + package. + """ + phys_writer.write(PACKAGE_URI.rels_uri, pkg_rels.xml) + + +class _ContentTypesItem(object): + """ + Service class that composes a content types item ([Content_Types].xml) + based on a list of parts. Not meant to be instantiated directly, its + single interface method is xml_for(), e.g. + ``_ContentTypesItem.xml_for(parts)``. + """ + def __init__(self): + self._defaults = CaseInsensitiveDict() + self._overrides = dict() + + @property + def blob(self): + """ + Return XML form of this content types item, suitable for storage as + ``[Content_Types].xml`` in an OPC package. + """ + return serialize_part_xml(self._element) + + @classmethod + def from_parts(cls, parts): + """ + Return content types XML mapping each part in *parts* to the + appropriate content type and suitable for storage as + ``[Content_Types].xml`` in an OPC package. + """ + cti = cls() + cti._defaults['rels'] = CT.OPC_RELATIONSHIPS + cti._defaults['xml'] = CT.XML + for part in parts: + cti._add_content_type(part.partname, part.content_type) + return cti + + def _add_content_type(self, partname, content_type): + """ + Add a content type for the part with *partname* and *content_type*, + using a default or override as appropriate. + """ + ext = partname.ext + if (ext.lower(), content_type) in default_content_types: + self._defaults[ext] = content_type + else: + self._overrides[partname] = content_type + + @property + def _element(self): + """ + Return XML form of this content types item, suitable for storage as + ``[Content_Types].xml`` in an OPC package. Although the sequence of + elements is not strictly significant, as an aid to testing and + readability Default elements are sorted by extension and Override + elements are sorted by partname. + """ + _types_elm = CT_Types.new() + for ext in sorted(self._defaults.keys()): + _types_elm.add_default(ext, self._defaults[ext]) + for partname in sorted(self._overrides.keys()): + _types_elm.add_override(partname, self._overrides[partname]) + return _types_elm diff --git a/build/lib/docx/opc/rel.py b/build/lib/docx/opc/rel.py new file mode 100644 index 000000000..7dba2af8e --- /dev/null +++ b/build/lib/docx/opc/rel.py @@ -0,0 +1,170 @@ +# encoding: utf-8 + +""" +Relationship-related objects. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .oxml import CT_Relationships + + +class Relationships(dict): + """ + Collection object for |_Relationship| instances, having list semantics. + """ + def __init__(self, baseURI): + super(Relationships, self).__init__() + self._baseURI = baseURI + self._target_parts_by_rId = {} + + def add_relationship(self, reltype, target, rId, is_external=False): + """ + Return a newly added |_Relationship| instance. + """ + rel = _Relationship(rId, reltype, target, self._baseURI, is_external) + self[rId] = rel + if not is_external: + self._target_parts_by_rId[rId] = target + return rel + + def get_or_add(self, reltype, target_part): + """ + Return relationship of *reltype* to *target_part*, newly added if not + already present in collection. + """ + rel = self._get_matching(reltype, target_part) + if rel is None: + rId = self._next_rId + rel = self.add_relationship(reltype, target_part, rId) + return rel + + def get_or_add_ext_rel(self, reltype, target_ref): + """ + Return rId of external relationship of *reltype* to *target_ref*, + newly added if not already present in collection. + """ + rel = self._get_matching(reltype, target_ref, is_external=True) + if rel is None: + rId = self._next_rId + rel = self.add_relationship( + reltype, target_ref, rId, is_external=True + ) + return rel.rId + + def part_with_reltype(self, reltype): + """ + Return target part of rel with matching *reltype*, raising |KeyError| + if not found and |ValueError| if more than one matching relationship + is found. + """ + rel = self._get_rel_of_type(reltype) + return rel.target_part + + @property + def related_parts(self): + """ + dict mapping rIds to target parts for all the internal relationships + in the collection. + """ + return self._target_parts_by_rId + + @property + def xml(self): + """ + Serialize this relationship collection into XML suitable for storage + as a .rels file in an OPC package. + """ + rels_elm = CT_Relationships.new() + for rel in self.values(): + rels_elm.add_rel( + rel.rId, rel.reltype, rel.target_ref, rel.is_external + ) + return rels_elm.xml + + def _get_matching(self, reltype, target, is_external=False): + """ + Return relationship of matching *reltype*, *target*, and + *is_external* from collection, or None if not found. + """ + def matches(rel, reltype, target, is_external): + if rel.reltype != reltype: + return False + if rel.is_external != is_external: + return False + rel_target = rel.target_ref if rel.is_external else rel.target_part + if rel_target != target: + return False + return True + + for rel in self.values(): + if matches(rel, reltype, target, is_external): + return rel + return None + + def _get_rel_of_type(self, reltype): + """ + Return single relationship of type *reltype* from the collection. + Raises |KeyError| if no matching relationship is found. Raises + |ValueError| if more than one matching relationship is found. + """ + matching = [rel for rel in self.values() if rel.reltype == reltype] + if len(matching) == 0: + tmpl = "no relationship of type '%s' in collection" + raise KeyError(tmpl % reltype) + if len(matching) > 1: + tmpl = "multiple relationships of type '%s' in collection" + raise ValueError(tmpl % reltype) + return matching[0] + + @property + def _next_rId(self): + """ + Next available rId in collection, starting from 'rId1' and making use + of any gaps in numbering, e.g. 'rId2' for rIds ['rId1', 'rId3']. + """ + for n in range(1, len(self)+2): + rId_candidate = 'rId%d' % n # like 'rId19' + if rId_candidate not in self: + return rId_candidate + + +class _Relationship(object): + """ + Value object for relationship to part. + """ + def __init__(self, rId, reltype, target, baseURI, external=False): + super(_Relationship, self).__init__() + self._rId = rId + self._reltype = reltype + self._target = target + self._baseURI = baseURI + self._is_external = bool(external) + + @property + def is_external(self): + return self._is_external + + @property + def reltype(self): + return self._reltype + + @property + def rId(self): + return self._rId + + @property + def target_part(self): + if self._is_external: + raise ValueError("target_part property on _Relationship is undef" + "ined when target mode is External") + return self._target + + @property + def target_ref(self): + if self._is_external: + return self._target + else: + return self._target.partname.relative_ref(self._baseURI) diff --git a/build/lib/docx/opc/shared.py b/build/lib/docx/opc/shared.py new file mode 100644 index 000000000..55344483d --- /dev/null +++ b/build/lib/docx/opc/shared.py @@ -0,0 +1,47 @@ +# encoding: utf-8 + +""" +Objects shared by opc modules. +""" + +from __future__ import absolute_import, print_function, unicode_literals + + +class CaseInsensitiveDict(dict): + """ + Mapping type that behaves like dict except that it matches without respect + to the case of the key. E.g. cid['A'] == cid['a']. Note this is not + general-purpose, just complete enough to satisfy opc package needs. It + assumes str keys, and that it is created empty; keys passed in constructor + are not accounted for + """ + def __contains__(self, key): + return super(CaseInsensitiveDict, self).__contains__(key.lower()) + + def __getitem__(self, key): + return super(CaseInsensitiveDict, self).__getitem__(key.lower()) + + def __setitem__(self, key, value): + return super(CaseInsensitiveDict, self).__setitem__( + key.lower(), value + ) + + +def lazyproperty(f): + """ + @lazyprop decorator. Decorated method will be called only on first access + to calculate a cached property value. After that, the cached value is + returned. + """ + cache_attr_name = '_%s' % f.__name__ # like '_foobar' for prop 'foobar' + docstring = f.__doc__ + + def get_prop_value(obj): + try: + return getattr(obj, cache_attr_name) + except AttributeError: + value = f(obj) + setattr(obj, cache_attr_name, value) + return value + + return property(get_prop_value, doc=docstring) diff --git a/build/lib/docx/opc/spec.py b/build/lib/docx/opc/spec.py new file mode 100644 index 000000000..60fc38564 --- /dev/null +++ b/build/lib/docx/opc/spec.py @@ -0,0 +1,29 @@ +# encoding: utf-8 + +""" +Provides mappings that embody aspects of the Open XML spec ISO/IEC 29500. +""" + +from .constants import CONTENT_TYPE as CT + + +default_content_types = ( + ('bin', CT.PML_PRINTER_SETTINGS), + ('bin', CT.SML_PRINTER_SETTINGS), + ('bin', CT.WML_PRINTER_SETTINGS), + ('bmp', CT.BMP), + ('emf', CT.X_EMF), + ('fntdata', CT.X_FONTDATA), + ('gif', CT.GIF), + ('jpe', CT.JPEG), + ('jpeg', CT.JPEG), + ('jpg', CT.JPEG), + ('png', CT.PNG), + ('rels', CT.OPC_RELATIONSHIPS), + ('tif', CT.TIFF), + ('tiff', CT.TIFF), + ('wdp', CT.MS_PHOTO), + ('wmf', CT.X_WMF), + ('xlsx', CT.SML_SHEET), + ('xml', CT.XML), +) diff --git a/build/lib/docx/oxml/__init__.py b/build/lib/docx/oxml/__init__.py new file mode 100644 index 000000000..fc6e18abc --- /dev/null +++ b/build/lib/docx/oxml/__init__.py @@ -0,0 +1,213 @@ +# encoding: utf-8 + +""" +Initializes oxml sub-package, including registering custom element classes +corresponding to Open XML elements. +""" + +from __future__ import absolute_import + +from lxml import etree + +from .ns import NamespacePrefixedTag, nsmap + + +# configure XML parser +element_class_lookup = etree.ElementNamespaceClassLookup() +oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) +oxml_parser.set_element_class_lookup(element_class_lookup) + + +def parse_xml(xml): + """ + Return root lxml element obtained by parsing XML character string in + *xml*, which can be either a Python 2.x string or unicode. The custom + parser is used, so custom element classes are produced for elements in + *xml* that have them. + """ + root_element = etree.fromstring(xml, oxml_parser) + return root_element + + +def register_element_cls(tag, cls): + """ + Register *cls* to be constructed when the oxml parser encounters an + element with matching *tag*. *tag* is a string of the form + ``nspfx:tagroot``, e.g. ``'w:document'``. + """ + nspfx, tagroot = tag.split(':') + namespace = element_class_lookup.get_namespace(nsmap[nspfx]) + namespace[tagroot] = cls + + +def OxmlElement(nsptag_str, attrs=None, nsdecls=None): + """ + Return a 'loose' lxml element having the tag specified by *nsptag_str*. + *nsptag_str* must contain the standard namespace prefix, e.g. 'a:tbl'. + The resulting element is an instance of the custom element class for this + tag name if one is defined. A dictionary of attribute values may be + provided as *attrs*; they are set if present. All namespaces defined in + the dict *nsdecls* are declared in the element using the key as the + prefix and the value as the namespace name. If *nsdecls* is not provided, + a single namespace declaration is added based on the prefix on + *nsptag_str*. + """ + nsptag = NamespacePrefixedTag(nsptag_str) + if nsdecls is None: + nsdecls = nsptag.nsmap + return oxml_parser.makeelement( + nsptag.clark_name, attrib=attrs, nsmap=nsdecls + ) + + +# =========================================================================== +# custom element class mappings +# =========================================================================== + +from .shared import CT_DecimalNumber, CT_OnOff, CT_String + + +from .coreprops import CT_CoreProperties +register_element_cls('cp:coreProperties', CT_CoreProperties) + +from .document import CT_Body, CT_Document +register_element_cls('w:body', CT_Body) +register_element_cls('w:document', CT_Document) + +from .numbering import ( + CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr +) +register_element_cls('w:abstractNumId', CT_DecimalNumber) +register_element_cls('w:ilvl', CT_DecimalNumber) +register_element_cls('w:lvlOverride', CT_NumLvl) +register_element_cls('w:num', CT_Num) +register_element_cls('w:numId', CT_DecimalNumber) +register_element_cls('w:numPr', CT_NumPr) +register_element_cls('w:numbering', CT_Numbering) +register_element_cls('w:startOverride', CT_DecimalNumber) + +from .section import CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType +register_element_cls('w:pgMar', CT_PageMar) +register_element_cls('w:pgSz', CT_PageSz) +register_element_cls('w:sectPr', CT_SectPr) +register_element_cls('w:type', CT_SectType) + +from .shape import ( + CT_Blip, CT_BlipFillProperties, CT_GraphicalObject, + CT_GraphicalObjectData, CT_Inline, CT_NonVisualDrawingProps, CT_Picture, + CT_PictureNonVisual, CT_Point2D, CT_PositiveSize2D, CT_ShapeProperties, + CT_Transform2D +) +register_element_cls('a:blip', CT_Blip) +register_element_cls('a:ext', CT_PositiveSize2D) +register_element_cls('a:graphic', CT_GraphicalObject) +register_element_cls('a:graphicData', CT_GraphicalObjectData) +register_element_cls('a:off', CT_Point2D) +register_element_cls('a:xfrm', CT_Transform2D) +register_element_cls('pic:blipFill', CT_BlipFillProperties) +register_element_cls('pic:cNvPr', CT_NonVisualDrawingProps) +register_element_cls('pic:nvPicPr', CT_PictureNonVisual) +register_element_cls('pic:pic', CT_Picture) +register_element_cls('pic:spPr', CT_ShapeProperties) +register_element_cls('wp:docPr', CT_NonVisualDrawingProps) +register_element_cls('wp:extent', CT_PositiveSize2D) +register_element_cls('wp:inline', CT_Inline) + +from .styles import CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles +register_element_cls('w:basedOn', CT_String) +register_element_cls('w:latentStyles', CT_LatentStyles) +register_element_cls('w:locked', CT_OnOff) +register_element_cls('w:lsdException', CT_LsdException) +register_element_cls('w:name', CT_String) +register_element_cls('w:next', CT_String) +register_element_cls('w:qFormat', CT_OnOff) +register_element_cls('w:semiHidden', CT_OnOff) +register_element_cls('w:style', CT_Style) +register_element_cls('w:styles', CT_Styles) +register_element_cls('w:uiPriority', CT_DecimalNumber) +register_element_cls('w:unhideWhenUsed', CT_OnOff) + +from .table import ( + CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, + CT_TblPr, CT_TblWidth, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge +) +register_element_cls('w:bidiVisual', CT_OnOff) +register_element_cls('w:gridCol', CT_TblGridCol) +register_element_cls('w:gridSpan', CT_DecimalNumber) +register_element_cls('w:tbl', CT_Tbl) +register_element_cls('w:tblGrid', CT_TblGrid) +register_element_cls('w:tblLayout', CT_TblLayoutType) +register_element_cls('w:tblPr', CT_TblPr) +register_element_cls('w:tblStyle', CT_String) +register_element_cls('w:tc', CT_Tc) +register_element_cls('w:tcPr', CT_TcPr) +register_element_cls('w:tcW', CT_TblWidth) +register_element_cls('w:tr', CT_Row) +register_element_cls('w:trHeight', CT_Height) +register_element_cls('w:trPr', CT_TrPr) +register_element_cls('w:vAlign', CT_VerticalJc) +register_element_cls('w:vMerge', CT_VMerge) + +from .text.font import ( + CT_Color, CT_Fonts, CT_Highlight, CT_HpsMeasure, CT_RPr, CT_Underline, + CT_VerticalAlignRun +) +register_element_cls('w:b', CT_OnOff) +register_element_cls('w:bCs', CT_OnOff) +register_element_cls('w:caps', CT_OnOff) +register_element_cls('w:color', CT_Color) +register_element_cls('w:cs', CT_OnOff) +register_element_cls('w:dstrike', CT_OnOff) +register_element_cls('w:emboss', CT_OnOff) +register_element_cls('w:highlight', CT_Highlight) +register_element_cls('w:i', CT_OnOff) +register_element_cls('w:iCs', CT_OnOff) +register_element_cls('w:imprint', CT_OnOff) +register_element_cls('w:noProof', CT_OnOff) +register_element_cls('w:oMath', CT_OnOff) +register_element_cls('w:outline', CT_OnOff) +register_element_cls('w:rFonts', CT_Fonts) +register_element_cls('w:rPr', CT_RPr) +register_element_cls('w:rStyle', CT_String) +register_element_cls('w:rtl', CT_OnOff) +register_element_cls('w:shadow', CT_OnOff) +register_element_cls('w:smallCaps', CT_OnOff) +register_element_cls('w:snapToGrid', CT_OnOff) +register_element_cls('w:specVanish', CT_OnOff) +register_element_cls('w:strike', CT_OnOff) +register_element_cls('w:sz', CT_HpsMeasure) +register_element_cls('w:u', CT_Underline) +register_element_cls('w:vanish', CT_OnOff) +register_element_cls('w:vertAlign', CT_VerticalAlignRun) +register_element_cls('w:webHidden', CT_OnOff) + +from .text.paragraph import CT_P +register_element_cls('w:p', CT_P) + +from .text.parfmt import ( + CT_Ind, CT_Jc, CT_PPr, CT_Spacing, CT_TabStop, CT_TabStops +) +register_element_cls('w:ind', CT_Ind) +register_element_cls('w:jc', CT_Jc) +register_element_cls('w:keepLines', CT_OnOff) +register_element_cls('w:keepNext', CT_OnOff) +register_element_cls('w:pageBreakBefore', CT_OnOff) +register_element_cls('w:pPr', CT_PPr) +register_element_cls('w:pStyle', CT_String) +register_element_cls('w:spacing', CT_Spacing) +register_element_cls('w:tab', CT_TabStop) +register_element_cls('w:tabs', CT_TabStops) +register_element_cls('w:widowControl', CT_OnOff) + +from .text.run import CT_Br, CT_R, CT_Text +register_element_cls('w:br', CT_Br) +register_element_cls('w:r', CT_R) +register_element_cls('w:t', CT_Text) + + +from .comments import CT_Comments,CT_Com, CT_CRE, CT_CRS, CT_CRef +register_element_cls('w:comments', CT_Comments) +register_element_cls('w:comment', CT_Com) +register_element_cls('w:commentRangeStart', CT_CRS) +register_element_cls('w:commentRangeEnd', CT_CRE) +register_element_cls('w:commentReference', CT_CRef) \ No newline at end of file diff --git a/build/lib/docx/oxml/comments.py b/build/lib/docx/oxml/comments.py new file mode 100644 index 000000000..c4df76308 --- /dev/null +++ b/build/lib/docx/oxml/comments.py @@ -0,0 +1,110 @@ +""" +Custom element classes related to the comments part +""" + +from . import OxmlElement +from .simpletypes import ST_DecimalNumber, ST_String +from docx.text.run import Run +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne +) + +class CT_Com(BaseOxmlElement): + """ + A ```` element, a container for Comment properties + """ + initials = RequiredAttribute('w:initials', ST_String) + _id = RequiredAttribute('w:id', ST_DecimalNumber) + date = RequiredAttribute('w:date', ST_String) + author = RequiredAttribute('w:author', ST_String) + + paragraph = ZeroOrOne('w:p', successors=('w:comment',)) + + @classmethod + def new(cls, initials, comm_id, date, author): + """ + Return a new ```` element having _id of *comm_id* and having + the passed params as meta data + """ + comment = OxmlElement('w:comment') + comment.initials = initials + comment.date = date + comment._id = comm_id + comment.author = author + return comment + + def _add_p(self, text): + _p = OxmlElement('w:p') + _r = _p.add_r() + run = Run(_r,self) + run.text = text + self._insert_paragraph(_p) + return _p + +class CT_Comments(BaseOxmlElement): + """ + A ```` element, a container for Comments properties + """ + comment = ZeroOrMore ('w:comment', successors=('w:comments',)) + + def add_comment(self,author, initials, date): + _next_id = self._next_commentId + comment = CT_Com.new(initials, _next_id, date, author) + comment = self._insert_comment(comment) + + return comment + + @property + def _next_commentId(self): + ids = self.xpath('./w:comment/@w:id') + len(ids) + _ids = [int(_str) for _str in ids] + _ids.sort() + + print(_ids) + try: + return _ids[-1] + 2 + except: + return 0 + + +class CT_CRS(BaseOxmlElement): + """ + A ```` element + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + @classmethod + def new(cls, _id): + commentRangeStart = OxmlElement('w:commentRangeStart') + commentRangeStart._id =_id + + return commentRangeStart + + + +class CT_CRE(BaseOxmlElement): + """ + A ``w:commentRangeEnd`` element + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + + @classmethod + def new(cls, _id): + commentRangeEnd = OxmlElement('w:commentRangeEnd') + commentRangeEnd._id =_id + return commentRangeEnd + + +class CT_CRef(BaseOxmlElement): + """ + w:commentReference + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + @classmethod + def new (cls, _id): + commentReference = OxmlElement('w:commentReference') + commentReference._id =_id + return commentReference diff --git a/build/lib/docx/oxml/coreprops.py b/build/lib/docx/oxml/coreprops.py new file mode 100644 index 000000000..0ba85d027 --- /dev/null +++ b/build/lib/docx/oxml/coreprops.py @@ -0,0 +1,317 @@ +# encoding: utf-8 + +"""Custom element classes for core properties-related XML elements""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import re + +from datetime import datetime, timedelta + +from docx.compat import is_string +from docx.oxml import parse_xml +from docx.oxml.ns import nsdecls, qn +from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne + + +class CT_CoreProperties(BaseOxmlElement): + """ + ```` element, the root element of the Core Properties + part stored as ``/docProps/core.xml``. Implements many of the Dublin Core + document metadata elements. String elements resolve to an empty string + ('') if the element is not present in the XML. String elements are + limited in length to 255 unicode characters. + """ + category = ZeroOrOne('cp:category', successors=()) + contentStatus = ZeroOrOne('cp:contentStatus', successors=()) + created = ZeroOrOne('dcterms:created', successors=()) + creator = ZeroOrOne('dc:creator', successors=()) + description = ZeroOrOne('dc:description', successors=()) + identifier = ZeroOrOne('dc:identifier', successors=()) + keywords = ZeroOrOne('cp:keywords', successors=()) + language = ZeroOrOne('dc:language', successors=()) + lastModifiedBy = ZeroOrOne('cp:lastModifiedBy', successors=()) + lastPrinted = ZeroOrOne('cp:lastPrinted', successors=()) + modified = ZeroOrOne('dcterms:modified', successors=()) + revision = ZeroOrOne('cp:revision', successors=()) + subject = ZeroOrOne('dc:subject', successors=()) + title = ZeroOrOne('dc:title', successors=()) + version = ZeroOrOne('cp:version', successors=()) + + _coreProperties_tmpl = ( + '\n' % nsdecls('cp', 'dc', 'dcterms') + ) + + @classmethod + def new(cls): + """ + Return a new ```` element + """ + xml = cls._coreProperties_tmpl + coreProperties = parse_xml(xml) + return coreProperties + + @property + def author_text(self): + """ + The text in the `dc:creator` child element. + """ + return self._text_of_element('creator') + + @author_text.setter + def author_text(self, value): + self._set_element_text('creator', value) + + @property + def category_text(self): + return self._text_of_element('category') + + @category_text.setter + def category_text(self, value): + self._set_element_text('category', value) + + @property + def comments_text(self): + return self._text_of_element('description') + + @comments_text.setter + def comments_text(self, value): + self._set_element_text('description', value) + + @property + def contentStatus_text(self): + return self._text_of_element('contentStatus') + + @contentStatus_text.setter + def contentStatus_text(self, value): + self._set_element_text('contentStatus', value) + + @property + def created_datetime(self): + return self._datetime_of_element('created') + + @created_datetime.setter + def created_datetime(self, value): + self._set_element_datetime('created', value) + + @property + def identifier_text(self): + return self._text_of_element('identifier') + + @identifier_text.setter + def identifier_text(self, value): + self._set_element_text('identifier', value) + + @property + def keywords_text(self): + return self._text_of_element('keywords') + + @keywords_text.setter + def keywords_text(self, value): + self._set_element_text('keywords', value) + + @property + def language_text(self): + return self._text_of_element('language') + + @language_text.setter + def language_text(self, value): + self._set_element_text('language', value) + + @property + def lastModifiedBy_text(self): + return self._text_of_element('lastModifiedBy') + + @lastModifiedBy_text.setter + def lastModifiedBy_text(self, value): + self._set_element_text('lastModifiedBy', value) + + @property + def lastPrinted_datetime(self): + return self._datetime_of_element('lastPrinted') + + @lastPrinted_datetime.setter + def lastPrinted_datetime(self, value): + self._set_element_datetime('lastPrinted', value) + + @property + def modified_datetime(self): + return self._datetime_of_element('modified') + + @modified_datetime.setter + def modified_datetime(self, value): + self._set_element_datetime('modified', value) + + @property + def revision_number(self): + """ + Integer value of revision property. + """ + revision = self.revision + if revision is None: + return 0 + revision_str = revision.text + try: + revision = int(revision_str) + except ValueError: + # non-integer revision strings also resolve to 0 + revision = 0 + # as do negative integers + if revision < 0: + revision = 0 + return revision + + @revision_number.setter + def revision_number(self, value): + """ + Set revision property to string value of integer *value*. + """ + if not isinstance(value, int) or value < 1: + tmpl = "revision property requires positive int, got '%s'" + raise ValueError(tmpl % value) + revision = self.get_or_add_revision() + revision.text = str(value) + + @property + def subject_text(self): + return self._text_of_element('subject') + + @subject_text.setter + def subject_text(self, value): + self._set_element_text('subject', value) + + @property + def title_text(self): + return self._text_of_element('title') + + @title_text.setter + def title_text(self, value): + self._set_element_text('title', value) + + @property + def version_text(self): + return self._text_of_element('version') + + @version_text.setter + def version_text(self, value): + self._set_element_text('version', value) + + def _datetime_of_element(self, property_name): + element = getattr(self, property_name) + if element is None: + return None + datetime_str = element.text + try: + return self._parse_W3CDTF_to_datetime(datetime_str) + except ValueError: + # invalid datetime strings are ignored + return None + + def _get_or_add(self, prop_name): + """ + Return element returned by 'get_or_add_' method for *prop_name*. + """ + get_or_add_method_name = 'get_or_add_%s' % prop_name + get_or_add_method = getattr(self, get_or_add_method_name) + element = get_or_add_method() + return element + + @classmethod + def _offset_dt(cls, dt, offset_str): + """ + Return a |datetime| instance that is offset from datetime *dt* by + the timezone offset specified in *offset_str*, a string like + ``'-07:00'``. + """ + match = cls._offset_pattern.match(offset_str) + if match is None: + raise ValueError( + "'%s' is not a valid offset string" % offset_str + ) + sign, hours_str, minutes_str = match.groups() + sign_factor = -1 if sign == '+' else 1 + hours = int(hours_str) * sign_factor + minutes = int(minutes_str) * sign_factor + td = timedelta(hours=hours, minutes=minutes) + return dt + td + + _offset_pattern = re.compile('([+-])(\d\d):(\d\d)') + + @classmethod + def _parse_W3CDTF_to_datetime(cls, w3cdtf_str): + # valid W3CDTF date cases: + # yyyy e.g. '2003' + # yyyy-mm e.g. '2003-12' + # yyyy-mm-dd e.g. '2003-12-31' + # UTC timezone e.g. '2003-12-31T10:14:55Z' + # numeric timezone e.g. '2003-12-31T10:14:55-08:00' + templates = ( + '%Y-%m-%dT%H:%M:%S', + '%Y-%m-%d', + '%Y-%m', + '%Y', + ) + # strptime isn't smart enough to parse literal timezone offsets like + # '-07:30', so we have to do it ourselves + parseable_part = w3cdtf_str[:19] + offset_str = w3cdtf_str[19:] + dt = None + for tmpl in templates: + try: + dt = datetime.strptime(parseable_part, tmpl) + except ValueError: + continue + if dt is None: + tmpl = "could not parse W3CDTF datetime string '%s'" + raise ValueError(tmpl % w3cdtf_str) + if len(offset_str) == 6: + return cls._offset_dt(dt, offset_str) + return dt + + def _set_element_datetime(self, prop_name, value): + """ + Set date/time value of child element having *prop_name* to *value*. + """ + if not isinstance(value, datetime): + tmpl = ( + "property requires object, got %s" + ) + raise ValueError(tmpl % type(value)) + element = self._get_or_add(prop_name) + dt_str = value.strftime('%Y-%m-%dT%H:%M:%SZ') + element.text = dt_str + if prop_name in ('created', 'modified'): + # These two require an explicit 'xsi:type="dcterms:W3CDTF"' + # attribute. The first and last line are a hack required to add + # the xsi namespace to the root element rather than each child + # element in which it is referenced + self.set(qn('xsi:foo'), 'bar') + element.set(qn('xsi:type'), 'dcterms:W3CDTF') + del self.attrib[qn('xsi:foo')] + + def _set_element_text(self, prop_name, value): + """Set string value of *name* property to *value*.""" + if not is_string(value): + value = str(value) + + if len(value) > 255: + tmpl = ( + "exceeded 255 char limit for property, got:\n\n'%s'" + ) + raise ValueError(tmpl % value) + element = self._get_or_add(prop_name) + element.text = value + + def _text_of_element(self, property_name): + """ + Return the text in the element matching *property_name*, or an empty + string if the element is not present or contains no text. + """ + element = getattr(self, property_name) + if element is None: + return '' + if element.text is None: + return '' + return element.text diff --git a/build/lib/docx/oxml/document.py b/build/lib/docx/oxml/document.py new file mode 100644 index 000000000..e1cb4ac55 --- /dev/null +++ b/build/lib/docx/oxml/document.py @@ -0,0 +1,59 @@ +# encoding: utf-8 + +""" +Custom element classes that correspond to the document part, e.g. +. +""" + +from .xmlchemy import BaseOxmlElement, ZeroOrOne, ZeroOrMore + + +class CT_Document(BaseOxmlElement): + """ + ```` element, the root element of a document.xml file. + """ + body = ZeroOrOne('w:body') + + @property + def sectPr_lst(self): + """ + Return a list containing a reference to each ```` element + in the document, in the order encountered. + """ + return self.xpath('.//w:sectPr') + + +class CT_Body(BaseOxmlElement): + """ + ````, the container element for the main document story in + ``document.xml``. + """ + p = ZeroOrMore('w:p', successors=('w:sectPr',)) + tbl = ZeroOrMore('w:tbl', successors=('w:sectPr',)) + sectPr = ZeroOrOne('w:sectPr', successors=()) + + def add_section_break(self): + """ + Return the current ```` element after adding a clone of it + in a new ```` element appended to the block content elements. + Note that the "current" ```` will always be the sentinel + sectPr in this case since we're always working at the end of the + block content. + """ + sentinel_sectPr = self.get_or_add_sectPr() + cloned_sectPr = sentinel_sectPr.clone() + p = self.add_p() + p.set_sectPr(cloned_sectPr) + return sentinel_sectPr + + def clear_content(self): + """ + Remove all content child elements from this element. Leave + the element if it is present. + """ + if self.sectPr is not None: + content_elms = self[:-1] + else: + content_elms = self[:] + for content_elm in content_elms: + self.remove(content_elm) diff --git a/build/lib/docx/oxml/exceptions.py b/build/lib/docx/oxml/exceptions.py new file mode 100644 index 000000000..4696f1e93 --- /dev/null +++ b/build/lib/docx/oxml/exceptions.py @@ -0,0 +1,16 @@ +# encoding: utf-8 + +""" +Exceptions for oxml sub-package +""" + + +class XmlchemyError(Exception): + """Generic error class.""" + + +class InvalidXmlError(XmlchemyError): + """ + Raised when invalid XML is encountered, such as on attempt to access a + missing required child element + """ diff --git a/build/lib/docx/oxml/ns.py b/build/lib/docx/oxml/ns.py new file mode 100644 index 000000000..e6f6a4acc --- /dev/null +++ b/build/lib/docx/oxml/ns.py @@ -0,0 +1,114 @@ +# encoding: utf-8 + +""" +Namespace-related objects. +""" + +from __future__ import absolute_import, print_function, unicode_literals + + +nsmap = { + 'a': ('http://schemas.openxmlformats.org/drawingml/2006/main'), + 'c': ('http://schemas.openxmlformats.org/drawingml/2006/chart'), + 'cp': ('http://schemas.openxmlformats.org/package/2006/metadata/core-pr' + 'operties'), + 'dc': ('http://purl.org/dc/elements/1.1/'), + 'dcmitype': ('http://purl.org/dc/dcmitype/'), + 'dcterms': ('http://purl.org/dc/terms/'), + 'dgm': ('http://schemas.openxmlformats.org/drawingml/2006/diagram'), + 'pic': ('http://schemas.openxmlformats.org/drawingml/2006/picture'), + 'r': ('http://schemas.openxmlformats.org/officeDocument/2006/relations' + 'hips'), + 'w': ('http://schemas.openxmlformats.org/wordprocessingml/2006/main'), + 'wp': ('http://schemas.openxmlformats.org/drawingml/2006/wordprocessing' + 'Drawing'), + 'xml': ('http://www.w3.org/XML/1998/namespace'), + 'xsi': ('http://www.w3.org/2001/XMLSchema-instance'), +} + +pfxmap = dict((value, key) for key, value in nsmap.items()) + + +class NamespacePrefixedTag(str): + """ + Value object that knows the semantics of an XML tag having a namespace + prefix. + """ + def __new__(cls, nstag, *args): + return super(NamespacePrefixedTag, cls).__new__(cls, nstag) + + def __init__(self, nstag): + self._pfx, self._local_part = nstag.split(':') + self._ns_uri = nsmap[self._pfx] + + @property + def clark_name(self): + return '{%s}%s' % (self._ns_uri, self._local_part) + + @classmethod + def from_clark_name(cls, clark_name): + nsuri, local_name = clark_name[1:].split('}') + nstag = '%s:%s' % (pfxmap[nsuri], local_name) + return cls(nstag) + + @property + def local_part(self): + """ + Return the local part of the tag as a string. E.g. 'foobar' is + returned for tag 'f:foobar'. + """ + return self._local_part + + @property + def nsmap(self): + """ + Return a dict having a single member, mapping the namespace prefix of + this tag to it's namespace name (e.g. {'f': 'http://foo/bar'}). This + is handy for passing to xpath calls and other uses. + """ + return {self._pfx: self._ns_uri} + + @property + def nspfx(self): + """ + Return the string namespace prefix for the tag, e.g. 'f' is returned + for tag 'f:foobar'. + """ + return self._pfx + + @property + def nsuri(self): + """ + Return the namespace URI for the tag, e.g. 'http://foo/bar' would be + returned for tag 'f:foobar' if the 'f' prefix maps to + 'http://foo/bar' in nsmap. + """ + return self._ns_uri + + +def nsdecls(*prefixes): + """ + Return a string containing a namespace declaration for each of the + namespace prefix strings, e.g. 'p', 'ct', passed as *prefixes*. + """ + return ' '.join(['xmlns:%s="%s"' % (pfx, nsmap[pfx]) for pfx in prefixes]) + + +def nspfxmap(*nspfxs): + """ + Return a dict containing the subset namespace prefix mappings specified by + *nspfxs*. Any number of namespace prefixes can be supplied, e.g. + namespaces('a', 'r', 'p'). + """ + return dict((pfx, nsmap[pfx]) for pfx in nspfxs) + + +def qn(tag): + """ + Stands for "qualified name", a utility function to turn a namespace + prefixed tag name into a Clark-notation qualified tag name for lxml. For + example, ``qn('p:cSld')`` returns ``'{http://schemas.../main}cSld'``. + """ + prefix, tagroot = tag.split(':') + uri = nsmap[prefix] + return '{%s}%s' % (uri, tagroot) diff --git a/build/lib/docx/oxml/numbering.py b/build/lib/docx/oxml/numbering.py new file mode 100644 index 000000000..aeedfa9a0 --- /dev/null +++ b/build/lib/docx/oxml/numbering.py @@ -0,0 +1,131 @@ +# encoding: utf-8 + +""" +Custom element classes related to the numbering part +""" + +from . import OxmlElement +from .shared import CT_DecimalNumber +from .simpletypes import ST_DecimalNumber +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne +) + + +class CT_Num(BaseOxmlElement): + """ + ```` element, which represents a concrete list definition + instance, having a required child that references an + abstract numbering definition that defines most of the formatting details. + """ + abstractNumId = OneAndOnlyOne('w:abstractNumId') + lvlOverride = ZeroOrMore('w:lvlOverride') + numId = RequiredAttribute('w:numId', ST_DecimalNumber) + + def add_lvlOverride(self, ilvl): + """ + Return a newly added CT_NumLvl () element having its + ``ilvl`` attribute set to *ilvl*. + """ + return self._add_lvlOverride(ilvl=ilvl) + + @classmethod + def new(cls, num_id, abstractNum_id): + """ + Return a new ```` element having numId of *num_id* and having + a ```` child with val attribute set to + *abstractNum_id*. + """ + num = OxmlElement('w:num') + num.numId = num_id + abstractNumId = CT_DecimalNumber.new( + 'w:abstractNumId', abstractNum_id + ) + num.append(abstractNumId) + return num + + +class CT_NumLvl(BaseOxmlElement): + """ + ```` element, which identifies a level in a list + definition to override with settings it contains. + """ + startOverride = ZeroOrOne('w:startOverride', successors=('w:lvl',)) + ilvl = RequiredAttribute('w:ilvl', ST_DecimalNumber) + + def add_startOverride(self, val): + """ + Return a newly added CT_DecimalNumber element having tagname + ``w:startOverride`` and ``val`` attribute set to *val*. + """ + return self._add_startOverride(val=val) + + +class CT_NumPr(BaseOxmlElement): + """ + A ```` element, a container for numbering properties applied to + a paragraph. + """ + ilvl = ZeroOrOne('w:ilvl', successors=( + 'w:numId', 'w:numberingChange', 'w:ins' + )) + numId = ZeroOrOne('w:numId', successors=('w:numberingChange', 'w:ins')) + + # @ilvl.setter + # def _set_ilvl(self, val): + # """ + # Get or add a child and set its ``w:val`` attribute to *val*. + # """ + # ilvl = self.get_or_add_ilvl() + # ilvl.val = val + + # @numId.setter + # def numId(self, val): + # """ + # Get or add a child and set its ``w:val`` attribute to + # *val*. + # """ + # numId = self.get_or_add_numId() + # numId.val = val + + +class CT_Numbering(BaseOxmlElement): + """ + ```` element, the root element of a numbering part, i.e. + numbering.xml + """ + num = ZeroOrMore('w:num', successors=('w:numIdMacAtCleanup',)) + + def add_num(self, abstractNum_id): + """ + Return a newly added CT_Num () element referencing the + abstract numbering definition identified by *abstractNum_id*. + """ + next_num_id = self._next_numId + num = CT_Num.new(next_num_id, abstractNum_id) + return self._insert_num(num) + + def num_having_numId(self, numId): + """ + Return the ```` child element having ``numId`` attribute + matching *numId*. + """ + xpath = './w:num[@w:numId="%d"]' % numId + try: + return self.xpath(xpath)[0] + except IndexError: + raise KeyError('no element with numId %d' % numId) + + @property + def _next_numId(self): + """ + The first ``numId`` unused by a ```` element, starting at + 1 and filling any gaps in numbering between existing ```` + elements. + """ + numId_strs = self.xpath('./w:num/@w:numId') + num_ids = [int(numId_str) for numId_str in numId_strs] + for num in range(1, len(num_ids)+2): + if num not in num_ids: + break + return num diff --git a/build/lib/docx/oxml/section.py b/build/lib/docx/oxml/section.py new file mode 100644 index 000000000..cf76b67ed --- /dev/null +++ b/build/lib/docx/oxml/section.py @@ -0,0 +1,264 @@ +# encoding: utf-8 + +""" +Section-related custom element classes. +""" + +from __future__ import absolute_import, print_function + +from copy import deepcopy + +from ..enum.section import WD_ORIENTATION, WD_SECTION_START +from .simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure +from .xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrOne + + +class CT_PageMar(BaseOxmlElement): + """ + ```` element, defining page margins. + """ + top = OptionalAttribute('w:top', ST_SignedTwipsMeasure) + right = OptionalAttribute('w:right', ST_TwipsMeasure) + bottom = OptionalAttribute('w:bottom', ST_SignedTwipsMeasure) + left = OptionalAttribute('w:left', ST_TwipsMeasure) + header = OptionalAttribute('w:header', ST_TwipsMeasure) + footer = OptionalAttribute('w:footer', ST_TwipsMeasure) + gutter = OptionalAttribute('w:gutter', ST_TwipsMeasure) + + +class CT_PageSz(BaseOxmlElement): + """ + ```` element, defining page dimensions and orientation. + """ + w = OptionalAttribute('w:w', ST_TwipsMeasure) + h = OptionalAttribute('w:h', ST_TwipsMeasure) + orient = OptionalAttribute( + 'w:orient', WD_ORIENTATION, default=WD_ORIENTATION.PORTRAIT + ) + + +class CT_SectPr(BaseOxmlElement): + """ + ```` element, the container element for section properties. + """ + __child_sequence__ = ( + 'w:footnotePr', 'w:endnotePr', 'w:type', 'w:pgSz', 'w:pgMar', + 'w:paperSrc', 'w:pgBorders', 'w:lnNumType', 'w:pgNumType', 'w:cols', + 'w:formProt', 'w:vAlign', 'w:noEndnote', 'w:titlePg', + 'w:textDirection', 'w:bidi', 'w:rtlGutter', 'w:docGrid', + 'w:printerSettings', 'w:sectPrChange', + ) + type = ZeroOrOne('w:type', successors=( + __child_sequence__[__child_sequence__.index('w:type')+1:] + )) + pgSz = ZeroOrOne('w:pgSz', successors=( + __child_sequence__[__child_sequence__.index('w:pgSz')+1:] + )) + pgMar = ZeroOrOne('w:pgMar', successors=( + __child_sequence__[__child_sequence__.index('w:pgMar')+1:] + )) + + @property + def bottom_margin(self): + """ + The value of the ``w:bottom`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.bottom + + @bottom_margin.setter + def bottom_margin(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.bottom = value + + def clone(self): + """ + Return an exact duplicate of this ```` element tree + suitable for use in adding a section break. All rsid* attributes are + removed from the root ```` element. + """ + clone_sectPr = deepcopy(self) + clone_sectPr.attrib.clear() + return clone_sectPr + + @property + def footer(self): + """ + The value of the ``w:footer`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.footer + + @footer.setter + def footer(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.footer = value + + @property + def gutter(self): + """ + The value of the ``w:gutter`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.gutter + + @gutter.setter + def gutter(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.gutter = value + + @property + def header(self): + """ + The value of the ``w:header`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.header + + @header.setter + def header(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.header = value + + @property + def left_margin(self): + """ + The value of the ``w:left`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.left + + @left_margin.setter + def left_margin(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.left = value + + @property + def right_margin(self): + """ + The value of the ``w:right`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.right + + @right_margin.setter + def right_margin(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.right = value + + @property + def orientation(self): + """ + The member of the ``WD_ORIENTATION`` enumeration corresponding to the + value of the ``orient`` attribute of the ```` child element, + or ``WD_ORIENTATION.PORTRAIT`` if not present. + """ + pgSz = self.pgSz + if pgSz is None: + return WD_ORIENTATION.PORTRAIT + return pgSz.orient + + @orientation.setter + def orientation(self, value): + pgSz = self.get_or_add_pgSz() + pgSz.orient = value + + @property + def page_height(self): + """ + Value in EMU of the ``h`` attribute of the ```` child + element, or |None| if not present. + """ + pgSz = self.pgSz + if pgSz is None: + return None + return pgSz.h + + @page_height.setter + def page_height(self, value): + pgSz = self.get_or_add_pgSz() + pgSz.h = value + + @property + def page_width(self): + """ + Value in EMU of the ``w`` attribute of the ```` child + element, or |None| if not present. + """ + pgSz = self.pgSz + if pgSz is None: + return None + return pgSz.w + + @page_width.setter + def page_width(self, value): + pgSz = self.get_or_add_pgSz() + pgSz.w = value + + @property + def start_type(self): + """ + The member of the ``WD_SECTION_START`` enumeration corresponding to + the value of the ``val`` attribute of the ```` child element, + or ``WD_SECTION_START.NEW_PAGE`` if not present. + """ + type = self.type + if type is None or type.val is None: + return WD_SECTION_START.NEW_PAGE + return type.val + + @start_type.setter + def start_type(self, value): + if value is None or value is WD_SECTION_START.NEW_PAGE: + self._remove_type() + return + type = self.get_or_add_type() + type.val = value + + @property + def top_margin(self): + """ + The value of the ``w:top`` attribute in the ```` child + element, as a |Length| object, or |None| if either the element or the + attribute is not present. + """ + pgMar = self.pgMar + if pgMar is None: + return None + return pgMar.top + + @top_margin.setter + def top_margin(self, value): + pgMar = self.get_or_add_pgMar() + pgMar.top = value + + +class CT_SectType(BaseOxmlElement): + """ + ```` element, defining the section start type. + """ + val = OptionalAttribute('w:val', WD_SECTION_START) diff --git a/build/lib/docx/oxml/shape.py b/build/lib/docx/oxml/shape.py new file mode 100644 index 000000000..77ca7db8a --- /dev/null +++ b/build/lib/docx/oxml/shape.py @@ -0,0 +1,284 @@ +# encoding: utf-8 + +""" +Custom element classes for shape-related elements like ```` +""" + +from . import parse_xml +from .ns import nsdecls +from .simpletypes import ( + ST_Coordinate, ST_DrawingElementId, ST_PositiveCoordinate, + ST_RelationshipId, XsdString, XsdToken +) +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, OptionalAttribute, RequiredAttribute, + ZeroOrOne +) + + +class CT_Blip(BaseOxmlElement): + """ + ```` element, specifies image source and adjustments such as + alpha and tint. + """ + embed = OptionalAttribute('r:embed', ST_RelationshipId) + link = OptionalAttribute('r:link', ST_RelationshipId) + + +class CT_BlipFillProperties(BaseOxmlElement): + """ + ```` element, specifies picture properties + """ + blip = ZeroOrOne('a:blip', successors=( + 'a:srcRect', 'a:tile', 'a:stretch' + )) + + +class CT_GraphicalObject(BaseOxmlElement): + """ + ```` element, container for a DrawingML object + """ + graphicData = OneAndOnlyOne('a:graphicData') + + +class CT_GraphicalObjectData(BaseOxmlElement): + """ + ```` element, container for the XML of a DrawingML object + """ + pic = ZeroOrOne('pic:pic') + uri = RequiredAttribute('uri', XsdToken) + + +class CT_Inline(BaseOxmlElement): + """ + ```` element, container for an inline shape. + """ + extent = OneAndOnlyOne('wp:extent') + docPr = OneAndOnlyOne('wp:docPr') + graphic = OneAndOnlyOne('a:graphic') + + @classmethod + def new(cls, cx, cy, shape_id, pic): + """ + Return a new ```` element populated with the values passed + as parameters. + """ + inline = parse_xml(cls._inline_xml()) + inline.extent.cx = cx + inline.extent.cy = cy + inline.docPr.id = shape_id + inline.docPr.name = 'Picture %d' % shape_id + inline.graphic.graphicData.uri = ( + 'http://schemas.openxmlformats.org/drawingml/2006/picture' + ) + inline.graphic.graphicData._insert_pic(pic) + return inline + + @classmethod + def new_pic_inline(cls, shape_id, rId, filename, cx, cy): + """ + Return a new `wp:inline` element containing the `pic:pic` element + specified by the argument values. + """ + pic_id = 0 # Word doesn't seem to use this, but does not omit it + pic = CT_Picture.new(pic_id, filename, rId, cx, cy) + inline = cls.new(cx, cy, shape_id, pic) + inline.graphic.graphicData._insert_pic(pic) + return inline + + @classmethod + def _inline_xml(cls): + return ( + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + '' % nsdecls('wp', 'a', 'pic', 'r') + ) + + +class CT_NonVisualDrawingProps(BaseOxmlElement): + """ + Used for ```` element, and perhaps others. Specifies the id and + name of a DrawingML drawing. + """ + id = RequiredAttribute('id', ST_DrawingElementId) + name = RequiredAttribute('name', XsdString) + + +class CT_NonVisualPictureProperties(BaseOxmlElement): + """ + ```` element, specifies picture locking and resize + behaviors. + """ + + +class CT_Picture(BaseOxmlElement): + """ + ```` element, a DrawingML picture + """ + nvPicPr = OneAndOnlyOne('pic:nvPicPr') + blipFill = OneAndOnlyOne('pic:blipFill') + spPr = OneAndOnlyOne('pic:spPr') + + @classmethod + def new(cls, pic_id, filename, rId, cx, cy): + """ + Return a new ```` element populated with the minimal + contents required to define a viable picture element, based on the + values passed as parameters. + """ + pic = parse_xml(cls._pic_xml()) + pic.nvPicPr.cNvPr.id = pic_id + pic.nvPicPr.cNvPr.name = filename + pic.blipFill.blip.embed = rId + pic.spPr.cx = cx + pic.spPr.cy = cy + return pic + + @classmethod + def _pic_xml(cls): + return ( + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + '' % nsdecls('pic', 'a', 'r') + ) + + +class CT_PictureNonVisual(BaseOxmlElement): + """ + ```` element, non-visual picture properties + """ + cNvPr = OneAndOnlyOne('pic:cNvPr') + + +class CT_Point2D(BaseOxmlElement): + """ + Used for ```` element, and perhaps others. Specifies an x, y + coordinate (point). + """ + x = RequiredAttribute('x', ST_Coordinate) + y = RequiredAttribute('y', ST_Coordinate) + + +class CT_PositiveSize2D(BaseOxmlElement): + """ + Used for ```` element, and perhaps others later. Specifies the + size of a DrawingML drawing. + """ + cx = RequiredAttribute('cx', ST_PositiveCoordinate) + cy = RequiredAttribute('cy', ST_PositiveCoordinate) + + +class CT_PresetGeometry2D(BaseOxmlElement): + """ + ```` element, specifies an preset autoshape geometry, such + as ``rect``. + """ + + +class CT_RelativeRect(BaseOxmlElement): + """ + ```` element, specifying picture should fill containing + rectangle shape. + """ + + +class CT_ShapeProperties(BaseOxmlElement): + """ + ```` element, specifies size and shape of picture container. + """ + xfrm = ZeroOrOne('a:xfrm', successors=( + 'a:custGeom', 'a:prstGeom', 'a:ln', 'a:effectLst', 'a:effectDag', + 'a:scene3d', 'a:sp3d', 'a:extLst' + )) + + @property + def cx(self): + """ + Shape width as an instance of Emu, or None if not present. + """ + xfrm = self.xfrm + if xfrm is None: + return None + return xfrm.cx + + @cx.setter + def cx(self, value): + xfrm = self.get_or_add_xfrm() + xfrm.cx = value + + @property + def cy(self): + """ + Shape height as an instance of Emu, or None if not present. + """ + xfrm = self.xfrm + if xfrm is None: + return None + return xfrm.cy + + @cy.setter + def cy(self, value): + xfrm = self.get_or_add_xfrm() + xfrm.cy = value + + +class CT_StretchInfoProperties(BaseOxmlElement): + """ + ```` element, specifies how picture should fill its containing + shape. + """ + + +class CT_Transform2D(BaseOxmlElement): + """ + ```` element, specifies size and shape of picture container. + """ + off = ZeroOrOne('a:off', successors=('a:ext',)) + ext = ZeroOrOne('a:ext', successors=()) + + @property + def cx(self): + ext = self.ext + if ext is None: + return None + return ext.cx + + @cx.setter + def cx(self, value): + ext = self.get_or_add_ext() + ext.cx = value + + @property + def cy(self): + ext = self.ext + if ext is None: + return None + return ext.cy + + @cy.setter + def cy(self, value): + ext = self.get_or_add_ext() + ext.cy = value diff --git a/build/lib/docx/oxml/shared.py b/build/lib/docx/oxml/shared.py new file mode 100644 index 000000000..1e21ba366 --- /dev/null +++ b/build/lib/docx/oxml/shared.py @@ -0,0 +1,55 @@ +# encoding: utf-8 + +""" +Objects shared by modules in the docx.oxml subpackage. +""" + +from __future__ import absolute_import + +from . import OxmlElement +from .ns import qn +from .simpletypes import ST_DecimalNumber, ST_OnOff, ST_String +from .xmlchemy import BaseOxmlElement, OptionalAttribute, RequiredAttribute + + +class CT_DecimalNumber(BaseOxmlElement): + """ + Used for ````, ````, ```` and several + others, containing a text representation of a decimal number (e.g. 42) in + its ``val`` attribute. + """ + val = RequiredAttribute('w:val', ST_DecimalNumber) + + @classmethod + def new(cls, nsptagname, val): + """ + Return a new ``CT_DecimalNumber`` element having tagname *nsptagname* + and ``val`` attribute set to *val*. + """ + return OxmlElement(nsptagname, attrs={qn('w:val'): str(val)}) + + +class CT_OnOff(BaseOxmlElement): + """ + Used for ````, ```` elements and others, containing a bool-ish + string in its ``val`` attribute, xsd:boolean plus 'on' and 'off'. + """ + val = OptionalAttribute('w:val', ST_OnOff, default=True) + + +class CT_String(BaseOxmlElement): + """ + Used for ```` and ```` elements and others, + containing a style name in its ``val`` attribute. + """ + val = RequiredAttribute('w:val', ST_String) + + @classmethod + def new(cls, nsptagname, val): + """ + Return a new ``CT_String`` element with tagname *nsptagname* and + ``val`` attribute set to *val*. + """ + elm = OxmlElement(nsptagname) + elm.val = val + return elm diff --git a/build/lib/docx/oxml/simpletypes.py b/build/lib/docx/oxml/simpletypes.py new file mode 100644 index 000000000..400a23700 --- /dev/null +++ b/build/lib/docx/oxml/simpletypes.py @@ -0,0 +1,409 @@ +# encoding: utf-8 + +""" +Simple type classes, providing validation and format translation for values +stored in XML element attributes. Naming generally corresponds to the simple +type in the associated XML schema. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..exceptions import InvalidXmlError +from ..shared import Emu, Pt, RGBColor, Twips + + +class BaseSimpleType(object): + + @classmethod + def from_xml(cls, str_value): + return cls.convert_from_xml(str_value) + + @classmethod + def to_xml(cls, value): + cls.validate(value) + str_value = cls.convert_to_xml(value) + return str_value + + @classmethod + def validate_int(cls, value): + if not isinstance(value, int): + raise TypeError( + "value must be , got %s" % type(value) + ) + + @classmethod + def validate_int_in_range(cls, value, min_inclusive, max_inclusive): + cls.validate_int(value) + if value < min_inclusive or value > max_inclusive: + raise ValueError( + "value must be in range %d to %d inclusive, got %d" % + (min_inclusive, max_inclusive, value) + ) + + @classmethod + def validate_string(cls, value): + if isinstance(value, str): + return value + try: + if isinstance(value, basestring): + return value + except NameError: # means we're on Python 3 + pass + raise TypeError( + "value must be a string, got %s" % type(value) + ) + + +class BaseIntType(BaseSimpleType): + + @classmethod + def convert_from_xml(cls, str_value): + return int(str_value) + + @classmethod + def convert_to_xml(cls, value): + return str(value) + + @classmethod + def validate(cls, value): + cls.validate_int(value) + + +class BaseStringType(BaseSimpleType): + + @classmethod + def convert_from_xml(cls, str_value): + return str_value + + @classmethod + def convert_to_xml(cls, value): + return value + + @classmethod + def validate(cls, value): + cls.validate_string(value) + + +class BaseStringEnumerationType(BaseStringType): + + @classmethod + def validate(cls, value): + cls.validate_string(value) + if value not in cls._members: + raise ValueError( + "must be one of %s, got '%s'" % (cls._members, value) + ) + + +class XsdAnyUri(BaseStringType): + """ + There's a regular expression this is supposed to meet but so far thinking + spending cycles on validating wouldn't be worth it for the number of + programming errors it would catch. + """ + + +class XsdBoolean(BaseSimpleType): + + @classmethod + def convert_from_xml(cls, str_value): + if str_value not in ('1', '0', 'true', 'false'): + raise InvalidXmlError( + "value must be one of '1', '0', 'true' or 'false', got '%s'" + % str_value + ) + return str_value in ('1', 'true') + + @classmethod + def convert_to_xml(cls, value): + return {True: '1', False: '0'}[value] + + @classmethod + def validate(cls, value): + if value not in (True, False): + raise TypeError( + "only True or False (and possibly None) may be assigned, got" + " '%s'" % value + ) + + +class XsdId(BaseStringType): + """ + String that must begin with a letter or underscore and cannot contain any + colons. Not fully validated because not used in external API. + """ + pass + + +class XsdInt(BaseIntType): + + @classmethod + def validate(cls, value): + cls.validate_int_in_range(value, -2147483648, 2147483647) + + +class XsdLong(BaseIntType): + + @classmethod + def validate(cls, value): + cls.validate_int_in_range( + value, -9223372036854775808, 9223372036854775807 + ) + + +class XsdString(BaseStringType): + pass + + +class XsdStringEnumeration(BaseStringEnumerationType): + """ + Set of enumerated xsd:string values. + """ + + +class XsdToken(BaseStringType): + """ + xsd:string with whitespace collapsing, e.g. multiple spaces reduced to + one, leading and trailing space stripped. + """ + pass + + +class XsdUnsignedInt(BaseIntType): + + @classmethod + def validate(cls, value): + cls.validate_int_in_range(value, 0, 4294967295) + + +class XsdUnsignedLong(BaseIntType): + + @classmethod + def validate(cls, value): + cls.validate_int_in_range(value, 0, 18446744073709551615) + + +class ST_BrClear(XsdString): + + @classmethod + def validate(cls, value): + cls.validate_string(value) + valid_values = ('none', 'left', 'right', 'all') + if value not in valid_values: + raise ValueError( + "must be one of %s, got '%s'" % (valid_values, value) + ) + + +class ST_BrType(XsdString): + + @classmethod + def validate(cls, value): + cls.validate_string(value) + valid_values = ('page', 'column', 'textWrapping') + if value not in valid_values: + raise ValueError( + "must be one of %s, got '%s'" % (valid_values, value) + ) + + +class ST_Coordinate(BaseIntType): + + @classmethod + def convert_from_xml(cls, str_value): + if 'i' in str_value or 'm' in str_value or 'p' in str_value: + return ST_UniversalMeasure.convert_from_xml(str_value) + return Emu(int(str_value)) + + @classmethod + def validate(cls, value): + ST_CoordinateUnqualified.validate(value) + + +class ST_CoordinateUnqualified(XsdLong): + + @classmethod + def validate(cls, value): + cls.validate_int_in_range(value, -27273042329600, 27273042316900) + + +class ST_DecimalNumber(XsdInt): + pass + + +class ST_DrawingElementId(XsdUnsignedInt): + pass + + +class ST_HexColor(BaseStringType): + + @classmethod + def convert_from_xml(cls, str_value): + if str_value == 'auto': + return ST_HexColorAuto.AUTO + return RGBColor.from_string(str_value) + + @classmethod + def convert_to_xml(cls, value): + """ + Keep alpha hex numerals all uppercase just for consistency. + """ + # expecting 3-tuple of ints in range 0-255 + return '%02X%02X%02X' % value + + @classmethod + def validate(cls, value): + # must be an RGBColor object --- + if not isinstance(value, RGBColor): + raise ValueError( + "rgb color value must be RGBColor object, got %s %s" + % (type(value), value) + ) + + +class ST_HexColorAuto(XsdStringEnumeration): + """ + Value for `w:color/[@val="auto"] attribute setting + """ + AUTO = 'auto' + + _members = (AUTO,) + + +class ST_HpsMeasure(XsdUnsignedLong): + """ + Half-point measure, e.g. 24.0 represents 12.0 points. + """ + @classmethod + def convert_from_xml(cls, str_value): + if 'm' in str_value or 'n' in str_value or 'p' in str_value: + return ST_UniversalMeasure.convert_from_xml(str_value) + return Pt(int(str_value)/2.0) + + @classmethod + def convert_to_xml(cls, value): + emu = Emu(value) + half_points = int(emu.pt * 2) + return str(half_points) + + +class ST_Merge(XsdStringEnumeration): + """ + Valid values for attribute + """ + CONTINUE = 'continue' + RESTART = 'restart' + + _members = (CONTINUE, RESTART) + + +class ST_OnOff(XsdBoolean): + + @classmethod + def convert_from_xml(cls, str_value): + if str_value not in ('1', '0', 'true', 'false', 'on', 'off'): + raise InvalidXmlError( + "value must be one of '1', '0', 'true', 'false', 'on', or 'o" + "ff', got '%s'" % str_value + ) + return str_value in ('1', 'true', 'on') + + +class ST_PositiveCoordinate(XsdLong): + + @classmethod + def convert_from_xml(cls, str_value): + return Emu(int(str_value)) + + @classmethod + def validate(cls, value): + cls.validate_int_in_range(value, 0, 27273042316900) + + +class ST_RelationshipId(XsdString): + pass + + +class ST_SignedTwipsMeasure(XsdInt): + + @classmethod + def convert_from_xml(cls, str_value): + if 'i' in str_value or 'm' in str_value or 'p' in str_value: + return ST_UniversalMeasure.convert_from_xml(str_value) + return Twips(int(str_value)) + + @classmethod + def convert_to_xml(cls, value): + emu = Emu(value) + twips = emu.twips + return str(twips) + + +class ST_String(XsdString): + pass + + +class ST_TblLayoutType(XsdString): + + @classmethod + def validate(cls, value): + cls.validate_string(value) + valid_values = ('fixed', 'autofit') + if value not in valid_values: + raise ValueError( + "must be one of %s, got '%s'" % (valid_values, value) + ) + + +class ST_TblWidth(XsdString): + + @classmethod + def validate(cls, value): + cls.validate_string(value) + valid_values = ('auto', 'dxa', 'nil', 'pct') + if value not in valid_values: + raise ValueError( + "must be one of %s, got '%s'" % (valid_values, value) + ) + + +class ST_TwipsMeasure(XsdUnsignedLong): + + @classmethod + def convert_from_xml(cls, str_value): + if 'i' in str_value or 'm' in str_value or 'p' in str_value: + return ST_UniversalMeasure.convert_from_xml(str_value) + return Twips(int(str_value)) + + @classmethod + def convert_to_xml(cls, value): + emu = Emu(value) + twips = emu.twips + return str(twips) + + +class ST_UniversalMeasure(BaseSimpleType): + + @classmethod + def convert_from_xml(cls, str_value): + float_part, units_part = str_value[:-2], str_value[-2:] + quantity = float(float_part) + multiplier = { + 'mm': 36000, 'cm': 360000, 'in': 914400, 'pt': 12700, + 'pc': 152400, 'pi': 152400 + }[units_part] + emu_value = Emu(int(round(quantity * multiplier))) + return emu_value + + +class ST_VerticalAlignRun(XsdStringEnumeration): + """ + Valid values for `w:vertAlign/@val`. + """ + BASELINE = 'baseline' + SUPERSCRIPT = 'superscript' + SUBSCRIPT = 'subscript' + + _members = (BASELINE, SUPERSCRIPT, SUBSCRIPT) diff --git a/build/lib/docx/oxml/styles.py b/build/lib/docx/oxml/styles.py new file mode 100644 index 000000000..6f27e45eb --- /dev/null +++ b/build/lib/docx/oxml/styles.py @@ -0,0 +1,351 @@ +# encoding: utf-8 + +""" +Custom element classes related to the styles part +""" + +from ..enum.style import WD_STYLE_TYPE +from .simpletypes import ST_DecimalNumber, ST_OnOff, ST_String +from .xmlchemy import ( + BaseOxmlElement, OptionalAttribute, RequiredAttribute, ZeroOrMore, + ZeroOrOne +) + + +def styleId_from_name(name): + """ + Return the style id corresponding to *name*, taking into account + special-case names such as 'Heading 1'. + """ + return { + 'caption': 'Caption', + 'heading 1': 'Heading1', + 'heading 2': 'Heading2', + 'heading 3': 'Heading3', + 'heading 4': 'Heading4', + 'heading 5': 'Heading5', + 'heading 6': 'Heading6', + 'heading 7': 'Heading7', + 'heading 8': 'Heading8', + 'heading 9': 'Heading9', + }.get(name, name.replace(' ', '')) + + +class CT_LatentStyles(BaseOxmlElement): + """ + `w:latentStyles` element, defining behavior defaults for latent styles + and containing `w:lsdException` child elements that each override those + defaults for a named latent style. + """ + lsdException = ZeroOrMore('w:lsdException', successors=()) + + count = OptionalAttribute('w:count', ST_DecimalNumber) + defLockedState = OptionalAttribute('w:defLockedState', ST_OnOff) + defQFormat = OptionalAttribute('w:defQFormat', ST_OnOff) + defSemiHidden = OptionalAttribute('w:defSemiHidden', ST_OnOff) + defUIPriority = OptionalAttribute('w:defUIPriority', ST_DecimalNumber) + defUnhideWhenUsed = OptionalAttribute('w:defUnhideWhenUsed', ST_OnOff) + + def bool_prop(self, attr_name): + """ + Return the boolean value of the attribute having *attr_name*, or + |False| if not present. + """ + value = getattr(self, attr_name) + if value is None: + return False + return value + + def get_by_name(self, name): + """ + Return the `w:lsdException` child having *name*, or |None| if not + found. + """ + found = self.xpath('w:lsdException[@w:name="%s"]' % name) + if not found: + return None + return found[0] + + def set_bool_prop(self, attr_name, value): + """ + Set the on/off attribute having *attr_name* to *value*. + """ + setattr(self, attr_name, bool(value)) + + +class CT_LsdException(BaseOxmlElement): + """ + ```` element, defining override visibility behaviors for + a named latent style. + """ + locked = OptionalAttribute('w:locked', ST_OnOff) + name = RequiredAttribute('w:name', ST_String) + qFormat = OptionalAttribute('w:qFormat', ST_OnOff) + semiHidden = OptionalAttribute('w:semiHidden', ST_OnOff) + uiPriority = OptionalAttribute('w:uiPriority', ST_DecimalNumber) + unhideWhenUsed = OptionalAttribute('w:unhideWhenUsed', ST_OnOff) + + def delete(self): + """ + Remove this `w:lsdException` element from the XML document. + """ + self.getparent().remove(self) + + def on_off_prop(self, attr_name): + """ + Return the boolean value of the attribute having *attr_name*, or + |None| if not present. + """ + return getattr(self, attr_name) + + def set_on_off_prop(self, attr_name, value): + """ + Set the on/off attribute having *attr_name* to *value*. + """ + setattr(self, attr_name, value) + + +class CT_Style(BaseOxmlElement): + """ + A ```` element, representing a style definition + """ + _tag_seq = ( + 'w:name', 'w:aliases', 'w:basedOn', 'w:next', 'w:link', + 'w:autoRedefine', 'w:hidden', 'w:uiPriority', 'w:semiHidden', + 'w:unhideWhenUsed', 'w:qFormat', 'w:locked', 'w:personal', + 'w:personalCompose', 'w:personalReply', 'w:rsid', 'w:pPr', 'w:rPr', + 'w:tblPr', 'w:trPr', 'w:tcPr', 'w:tblStylePr' + ) + name = ZeroOrOne('w:name', successors=_tag_seq[1:]) + basedOn = ZeroOrOne('w:basedOn', successors=_tag_seq[3:]) + next = ZeroOrOne('w:next', successors=_tag_seq[4:]) + uiPriority = ZeroOrOne('w:uiPriority', successors=_tag_seq[8:]) + semiHidden = ZeroOrOne('w:semiHidden', successors=_tag_seq[9:]) + unhideWhenUsed = ZeroOrOne('w:unhideWhenUsed', successors=_tag_seq[10:]) + qFormat = ZeroOrOne('w:qFormat', successors=_tag_seq[11:]) + locked = ZeroOrOne('w:locked', successors=_tag_seq[12:]) + pPr = ZeroOrOne('w:pPr', successors=_tag_seq[17:]) + rPr = ZeroOrOne('w:rPr', successors=_tag_seq[18:]) + del _tag_seq + + type = OptionalAttribute('w:type', WD_STYLE_TYPE) + styleId = OptionalAttribute('w:styleId', ST_String) + default = OptionalAttribute('w:default', ST_OnOff) + customStyle = OptionalAttribute('w:customStyle', ST_OnOff) + + @property + def basedOn_val(self): + """ + Value of `w:basedOn/@w:val` or |None| if not present. + """ + basedOn = self.basedOn + if basedOn is None: + return None + return basedOn.val + + @basedOn_val.setter + def basedOn_val(self, value): + if value is None: + self._remove_basedOn() + else: + self.get_or_add_basedOn().val = value + + @property + def base_style(self): + """ + Sibling CT_Style element this style is based on or |None| if no base + style or base style not found. + """ + basedOn = self.basedOn + if basedOn is None: + return None + styles = self.getparent() + base_style = styles.get_by_id(basedOn.val) + if base_style is None: + return None + return base_style + + def delete(self): + """ + Remove this `w:style` element from its parent `w:styles` element. + """ + self.getparent().remove(self) + + @property + def locked_val(self): + """ + Value of `w:locked/@w:val` or |False| if not present. + """ + locked = self.locked + if locked is None: + return False + return locked.val + + @locked_val.setter + def locked_val(self, value): + self._remove_locked() + if bool(value) is True: + locked = self._add_locked() + locked.val = value + + @property + def name_val(self): + """ + Value of ```` child or |None| if not present. + """ + name = self.name + if name is None: + return None + return name.val + + @name_val.setter + def name_val(self, value): + self._remove_name() + if value is not None: + name = self._add_name() + name.val = value + + @property + def next_style(self): + """ + Sibling CT_Style element identified by the value of `w:name/@w:val` + or |None| if no value is present or no style with that style id + is found. + """ + next = self.next + if next is None: + return None + styles = self.getparent() + return styles.get_by_id(next.val) # None if not found + + @property + def qFormat_val(self): + """ + Value of `w:qFormat/@w:val` or |False| if not present. + """ + qFormat = self.qFormat + if qFormat is None: + return False + return qFormat.val + + @qFormat_val.setter + def qFormat_val(self, value): + self._remove_qFormat() + if bool(value): + self._add_qFormat() + + @property + def semiHidden_val(self): + """ + Value of ```` child or |False| if not present. + """ + semiHidden = self.semiHidden + if semiHidden is None: + return False + return semiHidden.val + + @semiHidden_val.setter + def semiHidden_val(self, value): + self._remove_semiHidden() + if bool(value) is True: + semiHidden = self._add_semiHidden() + semiHidden.val = value + + @property + def uiPriority_val(self): + """ + Value of ```` child or |None| if not present. + """ + uiPriority = self.uiPriority + if uiPriority is None: + return None + return uiPriority.val + + @uiPriority_val.setter + def uiPriority_val(self, value): + self._remove_uiPriority() + if value is not None: + uiPriority = self._add_uiPriority() + uiPriority.val = value + + @property + def unhideWhenUsed_val(self): + """ + Value of `w:unhideWhenUsed/@w:val` or |False| if not present. + """ + unhideWhenUsed = self.unhideWhenUsed + if unhideWhenUsed is None: + return False + return unhideWhenUsed.val + + @unhideWhenUsed_val.setter + def unhideWhenUsed_val(self, value): + self._remove_unhideWhenUsed() + if bool(value) is True: + unhideWhenUsed = self._add_unhideWhenUsed() + unhideWhenUsed.val = value + + +class CT_Styles(BaseOxmlElement): + """ + ```` element, the root element of a styles part, i.e. + styles.xml + """ + _tag_seq = ('w:docDefaults', 'w:latentStyles', 'w:style') + latentStyles = ZeroOrOne('w:latentStyles', successors=_tag_seq[2:]) + style = ZeroOrMore('w:style', successors=()) + del _tag_seq + + def add_style_of_type(self, name, style_type, builtin): + """ + Return a newly added `w:style` element having *name* and + *style_type*. `w:style/@customStyle` is set based on the value of + *builtin*. + """ + style = self.add_style() + style.type = style_type + style.customStyle = None if builtin else True + style.styleId = styleId_from_name(name) + style.name_val = name + return style + + def default_for(self, style_type): + """ + Return `w:style[@w:type="*{style_type}*][-1]` or |None| if not found. + """ + default_styles_for_type = [ + s for s in self._iter_styles() + if s.type == style_type and s.default + ] + if not default_styles_for_type: + return None + # spec calls for last default in document order + return default_styles_for_type[-1] + + def get_by_id(self, styleId): + """ + Return the ```` child element having ``styleId`` attribute + matching *styleId*, or |None| if not found. + """ + xpath = 'w:style[@w:styleId="%s"]' % styleId + try: + return self.xpath(xpath)[0] + except IndexError: + return None + + def get_by_name(self, name): + """ + Return the ```` child element having ```` child + element with value *name*, or |None| if not found. + """ + xpath = 'w:style[w:name/@w:val="%s"]' % name + try: + return self.xpath(xpath)[0] + except IndexError: + return None + + def _iter_styles(self): + """ + Generate each of the `w:style` child elements in document order. + """ + return (style for style in self.xpath('w:style')) diff --git a/build/lib/docx/oxml/table.py b/build/lib/docx/oxml/table.py new file mode 100644 index 000000000..95f9c6243 --- /dev/null +++ b/build/lib/docx/oxml/table.py @@ -0,0 +1,895 @@ +# encoding: utf-8 + +"""Custom element classes for tables""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from . import parse_xml +from ..enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE +from ..exceptions import InvalidSpanError +from .ns import nsdecls, qn +from ..shared import Emu, Twips +from .simpletypes import ( + ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt +) +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, OneOrMore, OptionalAttribute, + RequiredAttribute, ZeroOrOne, ZeroOrMore +) + + +class CT_Height(BaseOxmlElement): + """ + Used for ```` to specify a row height and row height rule. + """ + val = OptionalAttribute('w:val', ST_TwipsMeasure) + hRule = OptionalAttribute('w:hRule', WD_ROW_HEIGHT_RULE) + + +class CT_Row(BaseOxmlElement): + """ + ```` element + """ + tblPrEx = ZeroOrOne('w:tblPrEx') # custom inserter below + trPr = ZeroOrOne('w:trPr') # custom inserter below + tc = ZeroOrMore('w:tc') + + def tc_at_grid_col(self, idx): + """ + The ```` element appearing at grid column *idx*. Raises + |ValueError| if no ``w:tc`` element begins at that grid column. + """ + grid_col = 0 + for tc in self.tc_lst: + if grid_col == idx: + return tc + grid_col += tc.grid_span + if grid_col > idx: + raise ValueError('no cell on grid column %d' % idx) + raise ValueError('index out of bounds') + + @property + def tr_idx(self): + """ + The index of this ```` element within its parent ```` + element. + """ + return self.getparent().tr_lst.index(self) + + @property + def trHeight_hRule(self): + """ + Return the value of `w:trPr/w:trHeight@w:hRule`, or |None| if not + present. + """ + trPr = self.trPr + if trPr is None: + return None + return trPr.trHeight_hRule + + @trHeight_hRule.setter + def trHeight_hRule(self, value): + trPr = self.get_or_add_trPr() + trPr.trHeight_hRule = value + + @property + def trHeight_val(self): + """ + Return the value of `w:trPr/w:trHeight@w:val`, or |None| if not + present. + """ + trPr = self.trPr + if trPr is None: + return None + return trPr.trHeight_val + + @trHeight_val.setter + def trHeight_val(self, value): + trPr = self.get_or_add_trPr() + trPr.trHeight_val = value + + def _insert_tblPrEx(self, tblPrEx): + self.insert(0, tblPrEx) + + def _insert_trPr(self, trPr): + tblPrEx = self.tblPrEx + if tblPrEx is not None: + tblPrEx.addnext(trPr) + else: + self.insert(0, trPr) + + def _new_tc(self): + return CT_Tc.new() + + +class CT_Tbl(BaseOxmlElement): + """ + ```` element + """ + tblPr = OneAndOnlyOne('w:tblPr') + tblGrid = OneAndOnlyOne('w:tblGrid') + tr = ZeroOrMore('w:tr') + + @property + def bidiVisual_val(self): + """ + Value of `w:tblPr/w:bidiVisual/@w:val` or |None| if not present. + Controls whether table cells are displayed right-to-left or + left-to-right. + """ + bidiVisual = self.tblPr.bidiVisual + if bidiVisual is None: + return None + return bidiVisual.val + + @bidiVisual_val.setter + def bidiVisual_val(self, value): + tblPr = self.tblPr + if value is None: + tblPr._remove_bidiVisual() + else: + tblPr.get_or_add_bidiVisual().val = value + + @property + def col_count(self): + """ + The number of grid columns in this table. + """ + return len(self.tblGrid.gridCol_lst) + + def iter_tcs(self): + """ + Generate each of the `w:tc` elements in this table, left to right and + top to bottom. Each cell in the first row is generated, followed by + each cell in the second row, etc. + """ + for tr in self.tr_lst: + for tc in tr.tc_lst: + yield tc + + @classmethod + def new_tbl(cls, rows, cols, width): + """ + Return a new `w:tbl` element having *rows* rows and *cols* columns + with *width* distributed evenly between the columns. + """ + return parse_xml(cls._tbl_xml(rows, cols, width)) + + @property + def tblStyle_val(self): + """ + Value of `w:tblPr/w:tblStyle/@w:val` (a table style id) or |None| if + not present. + """ + tblStyle = self.tblPr.tblStyle + if tblStyle is None: + return None + return tblStyle.val + + @tblStyle_val.setter + def tblStyle_val(self, styleId): + """ + Set the value of `w:tblPr/w:tblStyle/@w:val` (a table style id) to + *styleId*. If *styleId* is None, remove the `w:tblStyle` element. + """ + tblPr = self.tblPr + tblPr._remove_tblStyle() + if styleId is None: + return + tblPr._add_tblStyle().val = styleId + + @classmethod + def _tbl_xml(cls, rows, cols, width): + col_width = Emu(width/cols) if cols > 0 else Emu(0) + return ( + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + '%s' # tblGrid + '%s' # trs + '\n' + ) % ( + nsdecls('w'), + cls._tblGrid_xml(cols, col_width), + cls._trs_xml(rows, cols, col_width) + ) + + @classmethod + def _tblGrid_xml(cls, col_count, col_width): + xml = ' \n' + for i in range(col_count): + xml += ' \n' % col_width.twips + xml += ' \n' + return xml + + @classmethod + def _trs_xml(cls, row_count, col_count, col_width): + xml = '' + for i in range(row_count): + xml += ( + ' \n' + '%s' + ' \n' + ) % cls._tcs_xml(col_count, col_width) + return xml + + @classmethod + def _tcs_xml(cls, col_count, col_width): + xml = '' + for i in range(col_count): + xml += ( + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ) % col_width.twips + return xml + + +class CT_TblGrid(BaseOxmlElement): + """ + ```` element, child of ````, holds ```` + elements that define column count, width, etc. + """ + gridCol = ZeroOrMore('w:gridCol', successors=('w:tblGridChange',)) + + +class CT_TblGridCol(BaseOxmlElement): + """ + ```` element, child of ````, defines a table + column. + """ + w = OptionalAttribute('w:w', ST_TwipsMeasure) + + @property + def gridCol_idx(self): + """ + The index of this ```` element within its parent + ```` element. + """ + return self.getparent().gridCol_lst.index(self) + + +class CT_TblLayoutType(BaseOxmlElement): + """ + ```` element, specifying whether column widths are fixed or + can be automatically adjusted based on content. + """ + type = OptionalAttribute('w:type', ST_TblLayoutType) + + +class CT_TblPr(BaseOxmlElement): + """ + ```` element, child of ````, holds child elements that + define table properties such as style and borders. + """ + _tag_seq = ( + 'w:tblStyle', 'w:tblpPr', 'w:tblOverlap', 'w:bidiVisual', + 'w:tblStyleRowBandSize', 'w:tblStyleColBandSize', 'w:tblW', 'w:jc', + 'w:tblCellSpacing', 'w:tblInd', 'w:tblBorders', 'w:shd', + 'w:tblLayout', 'w:tblCellMar', 'w:tblLook', 'w:tblCaption', + 'w:tblDescription', 'w:tblPrChange' + ) + tblStyle = ZeroOrOne('w:tblStyle', successors=_tag_seq[1:]) + bidiVisual = ZeroOrOne('w:bidiVisual', successors=_tag_seq[4:]) + jc = ZeroOrOne('w:jc', successors=_tag_seq[8:]) + tblLayout = ZeroOrOne('w:tblLayout', successors=_tag_seq[13:]) + del _tag_seq + + @property + def alignment(self): + """ + Member of :ref:`WdRowAlignment` enumeration or |None|, based on the + contents of the `w:val` attribute of `./w:jc`. |None| if no `w:jc` + element is present. + """ + jc = self.jc + if jc is None: + return None + return jc.val + + @alignment.setter + def alignment(self, value): + self._remove_jc() + if value is None: + return + jc = self.get_or_add_jc() + jc.val = value + + @property + def autofit(self): + """ + Return |False| if there is a ```` child with ``w:type`` + attribute set to ``'fixed'``. Otherwise return |True|. + """ + tblLayout = self.tblLayout + if tblLayout is None: + return True + return False if tblLayout.type == 'fixed' else True + + @autofit.setter + def autofit(self, value): + tblLayout = self.get_or_add_tblLayout() + tblLayout.type = 'autofit' if value else 'fixed' + + @property + def style(self): + """ + Return the value of the ``val`` attribute of the ```` + child or |None| if not present. + """ + tblStyle = self.tblStyle + if tblStyle is None: + return None + return tblStyle.val + + @style.setter + def style(self, value): + self._remove_tblStyle() + if value is None: + return + self._add_tblStyle(val=value) + + +class CT_TblWidth(BaseOxmlElement): + """ + Used for ```` and ```` elements and many others, to + specify a table-related width. + """ + # the type for `w` attr is actually ST_MeasurementOrPercent, but using + # XsdInt for now because only dxa (twips) values are being used. It's not + # entirely clear what the semantics are for other values like -01.4mm + w = RequiredAttribute('w:w', XsdInt) + type = RequiredAttribute('w:type', ST_TblWidth) + + @property + def width(self): + """ + Return the EMU length value represented by the combined ``w:w`` and + ``w:type`` attributes. + """ + if self.type != 'dxa': + return None + return Twips(self.w) + + @width.setter + def width(self, value): + self.type = 'dxa' + self.w = Emu(value).twips + + +class CT_Tc(BaseOxmlElement): + """ + ```` table cell element + """ + tcPr = ZeroOrOne('w:tcPr') # bunches of successors, overriding insert + p = OneOrMore('w:p') + tbl = OneOrMore('w:tbl') + + @property + def bottom(self): + """ + The row index that marks the bottom extent of the vertical span of + this cell. This is one greater than the index of the bottom-most row + of the span, similar to how a slice of the cell's rows would be + specified. + """ + if self.vMerge is not None: + tc_below = self._tc_below + if tc_below is not None and tc_below.vMerge == ST_Merge.CONTINUE: + return tc_below.bottom + return self._tr_idx + 1 + + def clear_content(self): + """ + Remove all content child elements, preserving the ```` + element if present. Note that this leaves the ```` element in + an invalid state because it doesn't contain at least one block-level + element. It's up to the caller to add a ````child element as the + last content element. + """ + new_children = [] + tcPr = self.tcPr + if tcPr is not None: + new_children.append(tcPr) + self[:] = new_children + + @property + def grid_span(self): + """ + The integer number of columns this cell spans. Determined by + ./w:tcPr/w:gridSpan/@val, it defaults to 1. + """ + tcPr = self.tcPr + if tcPr is None: + return 1 + return tcPr.grid_span + + @grid_span.setter + def grid_span(self, value): + tcPr = self.get_or_add_tcPr() + tcPr.grid_span = value + + def iter_block_items(self): + """ + Generate a reference to each of the block-level content elements in + this cell, in the order they appear. + """ + block_item_tags = (qn('w:p'), qn('w:tbl'), qn('w:sdt')) + for child in self: + if child.tag in block_item_tags: + yield child + + @property + def left(self): + """ + The grid column index at which this ```` element appears. + """ + return self._grid_col + + def merge(self, other_tc): + """ + Return the top-left ```` element of a new span formed by + merging the rectangular region defined by using this tc element and + *other_tc* as diagonal corners. + """ + top, left, height, width = self._span_dimensions(other_tc) + top_tc = self._tbl.tr_lst[top].tc_at_grid_col(left) + top_tc._grow_to(width, height) + return top_tc + + @classmethod + def new(cls): + """ + Return a new ```` element, containing an empty paragraph as the + required EG_BlockLevelElt. + """ + return parse_xml( + '\n' + ' \n' + '' % nsdecls('w') + ) + + @property + def right(self): + """ + The grid column index that marks the right-side extent of the + horizontal span of this cell. This is one greater than the index of + the right-most column of the span, similar to how a slice of the + cell's columns would be specified. + """ + return self._grid_col + self.grid_span + + @property + def top(self): + """ + The top-most row index in the vertical span of this cell. + """ + if self.vMerge is None or self.vMerge == ST_Merge.RESTART: + return self._tr_idx + return self._tc_above.top + + @property + def vMerge(self): + """ + The value of the ./w:tcPr/w:vMerge/@val attribute, or |None| if the + w:vMerge element is not present. + """ + tcPr = self.tcPr + if tcPr is None: + return None + return tcPr.vMerge_val + + @vMerge.setter + def vMerge(self, value): + tcPr = self.get_or_add_tcPr() + tcPr.vMerge_val = value + + @property + def width(self): + """ + Return the EMU length value represented in the ``./w:tcPr/w:tcW`` + child element or |None| if not present. + """ + tcPr = self.tcPr + if tcPr is None: + return None + return tcPr.width + + @width.setter + def width(self, value): + tcPr = self.get_or_add_tcPr() + tcPr.width = value + + def _add_width_of(self, other_tc): + """ + Add the width of *other_tc* to this cell. Does nothing if either this + tc or *other_tc* does not have a specified width. + """ + if self.width and other_tc.width: + self.width += other_tc.width + + @property + def _grid_col(self): + """ + The grid column at which this cell begins. + """ + tr = self._tr + idx = tr.tc_lst.index(self) + preceding_tcs = tr.tc_lst[:idx] + return sum(tc.grid_span for tc in preceding_tcs) + + def _grow_to(self, width, height, top_tc=None): + """ + Grow this cell to *width* grid columns and *height* rows by expanding + horizontal spans and creating continuation cells to form vertical + spans. + """ + def vMerge_val(top_tc): + if top_tc is not self: + return ST_Merge.CONTINUE + if height == 1: + return None + return ST_Merge.RESTART + + top_tc = self if top_tc is None else top_tc + self._span_to_width(width, top_tc, vMerge_val(top_tc)) + if height > 1: + self._tc_below._grow_to(width, height-1, top_tc) + + def _insert_tcPr(self, tcPr): + """ + ``tcPr`` has a bunch of successors, but it comes first if it appears, + so just overriding and using insert(0, ...) rather than spelling out + successors. + """ + self.insert(0, tcPr) + return tcPr + + @property + def _is_empty(self): + """ + True if this cell contains only a single empty ```` element. + """ + block_items = list(self.iter_block_items()) + if len(block_items) > 1: + return False + p = block_items[0] # cell must include at least one element + if len(p.r_lst) == 0: + return True + return False + + def _move_content_to(self, other_tc): + """ + Append the content of this cell to *other_tc*, leaving this cell with + a single empty ```` element. + """ + if other_tc is self: + return + if self._is_empty: + return + other_tc._remove_trailing_empty_p() + # appending moves each element from self to other_tc + for block_element in self.iter_block_items(): + other_tc.append(block_element) + # add back the required minimum single empty element + self.append(self._new_p()) + + def _new_tbl(self): + return CT_Tbl.new() + + @property + def _next_tc(self): + """ + The `w:tc` element immediately following this one in this row, or + |None| if this is the last `w:tc` element in the row. + """ + following_tcs = self.xpath('./following-sibling::w:tc') + return following_tcs[0] if following_tcs else None + + def _remove(self): + """ + Remove this `w:tc` element from the XML tree. + """ + self.getparent().remove(self) + + def _remove_trailing_empty_p(self): + """ + Remove the last content element from this cell if it is an empty + ```` element. + """ + block_items = list(self.iter_block_items()) + last_content_elm = block_items[-1] + if last_content_elm.tag != qn('w:p'): + return + p = last_content_elm + if len(p.r_lst) > 0: + return + self.remove(p) + + def _span_dimensions(self, other_tc): + """ + Return a (top, left, height, width) 4-tuple specifying the extents of + the merged cell formed by using this tc and *other_tc* as opposite + corner extents. + """ + def raise_on_inverted_L(a, b): + if a.top == b.top and a.bottom != b.bottom: + raise InvalidSpanError('requested span not rectangular') + if a.left == b.left and a.right != b.right: + raise InvalidSpanError('requested span not rectangular') + + def raise_on_tee_shaped(a, b): + top_most, other = (a, b) if a.top < b.top else (b, a) + if top_most.top < other.top and top_most.bottom > other.bottom: + raise InvalidSpanError('requested span not rectangular') + + left_most, other = (a, b) if a.left < b.left else (b, a) + if left_most.left < other.left and left_most.right > other.right: + raise InvalidSpanError('requested span not rectangular') + + raise_on_inverted_L(self, other_tc) + raise_on_tee_shaped(self, other_tc) + + top = min(self.top, other_tc.top) + left = min(self.left, other_tc.left) + bottom = max(self.bottom, other_tc.bottom) + right = max(self.right, other_tc.right) + + return top, left, bottom - top, right - left + + def _span_to_width(self, grid_width, top_tc, vMerge): + """ + Incorporate and then remove `w:tc` elements to the right of this one + until this cell spans *grid_width*. Raises |ValueError| if + *grid_width* cannot be exactly achieved, such as when a merged cell + would drive the span width greater than *grid_width* or if not enough + grid columns are available to make this cell that wide. All content + from incorporated cells is appended to *top_tc*. The val attribute of + the vMerge element on the single remaining cell is set to *vMerge*. + If *vMerge* is |None|, the vMerge element is removed if present. + """ + self._move_content_to(top_tc) + while self.grid_span < grid_width: + self._swallow_next_tc(grid_width, top_tc) + self.vMerge = vMerge + + def _swallow_next_tc(self, grid_width, top_tc): + """ + Extend the horizontal span of this `w:tc` element to incorporate the + following `w:tc` element in the row and then delete that following + `w:tc` element. Any content in the following `w:tc` element is + appended to the content of *top_tc*. The width of the following + `w:tc` element is added to this one, if present. Raises + |InvalidSpanError| if the width of the resulting cell is greater than + *grid_width* or if there is no next `` element in the row. + """ + def raise_on_invalid_swallow(next_tc): + if next_tc is None: + raise InvalidSpanError('not enough grid columns') + if self.grid_span + next_tc.grid_span > grid_width: + raise InvalidSpanError('span is not rectangular') + + next_tc = self._next_tc + raise_on_invalid_swallow(next_tc) + next_tc._move_content_to(top_tc) + self._add_width_of(next_tc) + self.grid_span += next_tc.grid_span + next_tc._remove() + + @property + def _tbl(self): + """ + The tbl element this tc element appears in. + """ + return self.xpath('./ancestor::w:tbl[position()=1]')[0] + + @property + def _tc_above(self): + """ + The `w:tc` element immediately above this one in its grid column. + """ + return self._tr_above.tc_at_grid_col(self._grid_col) + + @property + def _tc_below(self): + """ + The tc element immediately below this one in its grid column. + """ + tr_below = self._tr_below + if tr_below is None: + return None + return tr_below.tc_at_grid_col(self._grid_col) + + @property + def _tr(self): + """ + The tr element this tc element appears in. + """ + return self.xpath('./ancestor::w:tr[position()=1]')[0] + + @property + def _tr_above(self): + """ + The tr element prior in sequence to the tr this cell appears in. + Raises |ValueError| if called on a cell in the top-most row. + """ + tr_lst = self._tbl.tr_lst + tr_idx = tr_lst.index(self._tr) + if tr_idx == 0: + raise ValueError('no tr above topmost tr') + return tr_lst[tr_idx-1] + + @property + def _tr_below(self): + """ + The tr element next in sequence after the tr this cell appears in, or + |None| if this cell appears in the last row. + """ + tr_lst = self._tbl.tr_lst + tr_idx = tr_lst.index(self._tr) + try: + return tr_lst[tr_idx+1] + except IndexError: + return None + + @property + def _tr_idx(self): + """ + The row index of the tr element this tc element appears in. + """ + return self._tbl.tr_lst.index(self._tr) + + +class CT_TcPr(BaseOxmlElement): + """ + ```` element, defining table cell properties + """ + _tag_seq = ( + 'w:cnfStyle', 'w:tcW', 'w:gridSpan', 'w:hMerge', 'w:vMerge', + 'w:tcBorders', 'w:shd', 'w:noWrap', 'w:tcMar', 'w:textDirection', + 'w:tcFitText', 'w:vAlign', 'w:hideMark', 'w:headers', 'w:cellIns', + 'w:cellDel', 'w:cellMerge', 'w:tcPrChange' + ) + tcW = ZeroOrOne('w:tcW', successors=_tag_seq[2:]) + gridSpan = ZeroOrOne('w:gridSpan', successors=_tag_seq[3:]) + vMerge = ZeroOrOne('w:vMerge', successors=_tag_seq[5:]) + vAlign = ZeroOrOne('w:vAlign', successors=_tag_seq[12:]) + del _tag_seq + + @property + def grid_span(self): + """ + The integer number of columns this cell spans. Determined by + ./w:gridSpan/@val, it defaults to 1. + """ + gridSpan = self.gridSpan + if gridSpan is None: + return 1 + return gridSpan.val + + @grid_span.setter + def grid_span(self, value): + self._remove_gridSpan() + if value > 1: + self.get_or_add_gridSpan().val = value + + @property + def vAlign_val(self): + """Value of `w:val` attribute on `w:vAlign` child. + + Value is |None| if `w:vAlign` child is not present. The `w:val` + attribute on `w:vAlign` is required. + """ + vAlign = self.vAlign + if vAlign is None: + return None + return vAlign.val + + @vAlign_val.setter + def vAlign_val(self, value): + if value is None: + self._remove_vAlign() + return + self.get_or_add_vAlign().val = value + + @property + def vMerge_val(self): + """ + The value of the ./w:vMerge/@val attribute, or |None| if the + w:vMerge element is not present. + """ + vMerge = self.vMerge + if vMerge is None: + return None + return vMerge.val + + @vMerge_val.setter + def vMerge_val(self, value): + self._remove_vMerge() + if value is not None: + self._add_vMerge().val = value + + @property + def width(self): + """ + Return the EMU length value represented in the ```` child + element or |None| if not present or its type is not 'dxa'. + """ + tcW = self.tcW + if tcW is None: + return None + return tcW.width + + @width.setter + def width(self, value): + tcW = self.get_or_add_tcW() + tcW.width = value + + +class CT_TrPr(BaseOxmlElement): + """ + ```` element, defining table row properties + """ + _tag_seq = ( + 'w:cnfStyle', 'w:divId', 'w:gridBefore', 'w:gridAfter', 'w:wBefore', + 'w:wAfter', 'w:cantSplit', 'w:trHeight', 'w:tblHeader', + 'w:tblCellSpacing', 'w:jc', 'w:hidden', 'w:ins', 'w:del', + 'w:trPrChange' + ) + trHeight = ZeroOrOne('w:trHeight', successors=_tag_seq[8:]) + del _tag_seq + + @property + def trHeight_hRule(self): + """ + Return the value of `w:trHeight@w:hRule`, or |None| if not present. + """ + trHeight = self.trHeight + if trHeight is None: + return None + return trHeight.hRule + + @trHeight_hRule.setter + def trHeight_hRule(self, value): + if value is None and self.trHeight is None: + return + trHeight = self.get_or_add_trHeight() + trHeight.hRule = value + + @property + def trHeight_val(self): + """ + Return the value of `w:trHeight@w:val`, or |None| if not present. + """ + trHeight = self.trHeight + if trHeight is None: + return None + return trHeight.val + + @trHeight_val.setter + def trHeight_val(self, value): + if value is None and self.trHeight is None: + return + trHeight = self.get_or_add_trHeight() + trHeight.val = value + + +class CT_VerticalJc(BaseOxmlElement): + """`w:vAlign` element, specifying vertical alignment of cell.""" + val = RequiredAttribute('w:val', WD_CELL_VERTICAL_ALIGNMENT) + + +class CT_VMerge(BaseOxmlElement): + """ + ```` element, specifying vertical merging behavior of a cell. + """ + val = OptionalAttribute('w:val', ST_Merge, default=ST_Merge.CONTINUE) diff --git a/build/lib/docx/oxml/text/__init__.py b/build/lib/docx/oxml/text/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/lib/docx/oxml/text/font.py b/build/lib/docx/oxml/text/font.py new file mode 100644 index 000000000..810ec2b30 --- /dev/null +++ b/build/lib/docx/oxml/text/font.py @@ -0,0 +1,320 @@ +# encoding: utf-8 + +""" +Custom element classes related to run properties (font). +""" + +from .. import parse_xml +from ...enum.dml import MSO_THEME_COLOR +from ...enum.text import WD_COLOR, WD_UNDERLINE +from ..ns import nsdecls, qn +from ..simpletypes import ( + ST_HexColor, ST_HpsMeasure, ST_String, ST_VerticalAlignRun +) +from ..xmlchemy import ( + BaseOxmlElement, OptionalAttribute, RequiredAttribute, ZeroOrOne +) + + +class CT_Color(BaseOxmlElement): + """ + `w:color` element, specifying the color of a font and perhaps other + objects. + """ + val = RequiredAttribute('w:val', ST_HexColor) + themeColor = OptionalAttribute('w:themeColor', MSO_THEME_COLOR) + + +class CT_Fonts(BaseOxmlElement): + """ + ```` element, specifying typeface name for the various language + types. + """ + ascii = OptionalAttribute('w:ascii', ST_String) + hAnsi = OptionalAttribute('w:hAnsi', ST_String) + + +class CT_Highlight(BaseOxmlElement): + """ + `w:highlight` element, specifying font highlighting/background color. + """ + val = RequiredAttribute('w:val', WD_COLOR) + + +class CT_HpsMeasure(BaseOxmlElement): + """ + Used for ```` element and others, specifying font size in + half-points. + """ + val = RequiredAttribute('w:val', ST_HpsMeasure) + + +class CT_RPr(BaseOxmlElement): + """ + ```` element, containing the properties for a run. + """ + _tag_seq = ( + 'w:rStyle', 'w:rFonts', 'w:b', 'w:bCs', 'w:i', 'w:iCs', 'w:caps', + 'w:smallCaps', 'w:strike', 'w:dstrike', 'w:outline', 'w:shadow', + 'w:emboss', 'w:imprint', 'w:noProof', 'w:snapToGrid', 'w:vanish', + 'w:webHidden', 'w:color', 'w:spacing', 'w:w', 'w:kern', 'w:position', + 'w:sz', 'w:szCs', 'w:highlight', 'w:u', 'w:effect', 'w:bdr', 'w:shd', + 'w:fitText', 'w:vertAlign', 'w:rtl', 'w:cs', 'w:em', 'w:lang', + 'w:eastAsianLayout', 'w:specVanish', 'w:oMath' + ) + rStyle = ZeroOrOne('w:rStyle', successors=_tag_seq[1:]) + rFonts = ZeroOrOne('w:rFonts', successors=_tag_seq[2:]) + b = ZeroOrOne('w:b', successors=_tag_seq[3:]) + bCs = ZeroOrOne('w:bCs', successors=_tag_seq[4:]) + i = ZeroOrOne('w:i', successors=_tag_seq[5:]) + iCs = ZeroOrOne('w:iCs', successors=_tag_seq[6:]) + caps = ZeroOrOne('w:caps', successors=_tag_seq[7:]) + smallCaps = ZeroOrOne('w:smallCaps', successors=_tag_seq[8:]) + strike = ZeroOrOne('w:strike', successors=_tag_seq[9:]) + dstrike = ZeroOrOne('w:dstrike', successors=_tag_seq[10:]) + outline = ZeroOrOne('w:outline', successors=_tag_seq[11:]) + shadow = ZeroOrOne('w:shadow', successors=_tag_seq[12:]) + emboss = ZeroOrOne('w:emboss', successors=_tag_seq[13:]) + imprint = ZeroOrOne('w:imprint', successors=_tag_seq[14:]) + noProof = ZeroOrOne('w:noProof', successors=_tag_seq[15:]) + snapToGrid = ZeroOrOne('w:snapToGrid', successors=_tag_seq[16:]) + vanish = ZeroOrOne('w:vanish', successors=_tag_seq[17:]) + webHidden = ZeroOrOne('w:webHidden', successors=_tag_seq[18:]) + color = ZeroOrOne('w:color', successors=_tag_seq[19:]) + sz = ZeroOrOne('w:sz', successors=_tag_seq[24:]) + highlight = ZeroOrOne('w:highlight', successors=_tag_seq[26:]) + u = ZeroOrOne('w:u', successors=_tag_seq[27:]) + vertAlign = ZeroOrOne('w:vertAlign', successors=_tag_seq[32:]) + rtl = ZeroOrOne('w:rtl', successors=_tag_seq[33:]) + cs = ZeroOrOne('w:cs', successors=_tag_seq[34:]) + specVanish = ZeroOrOne('w:specVanish', successors=_tag_seq[38:]) + oMath = ZeroOrOne('w:oMath', successors=_tag_seq[39:]) + del _tag_seq + + def _new_color(self): + """ + Override metaclass method to set `w:color/@val` to RGB black on + create. + """ + return parse_xml('' % nsdecls('w')) + + @property + def highlight_val(self): + """ + Value of `w:highlight/@val` attribute, specifying a font's highlight + color, or `None` if the text is not highlighted. + """ + highlight = self.highlight + if highlight is None: + return None + return highlight.val + + @highlight_val.setter + def highlight_val(self, value): + if value is None: + self._remove_highlight() + return + highlight = self.get_or_add_highlight() + highlight.val = value + + @property + def rFonts_ascii(self): + """ + The value of `w:rFonts/@w:ascii` or |None| if not present. Represents + the assigned typeface name. The rFonts element also specifies other + special-case typeface names; this method handles the case where just + the common name is required. + """ + rFonts = self.rFonts + if rFonts is None: + return None + return rFonts.ascii + + @rFonts_ascii.setter + def rFonts_ascii(self, value): + if value is None: + self._remove_rFonts() + return + rFonts = self.get_or_add_rFonts() + rFonts.ascii = value + + @property + def rFonts_hAnsi(self): + """ + The value of `w:rFonts/@w:hAnsi` or |None| if not present. + """ + rFonts = self.rFonts + if rFonts is None: + return None + return rFonts.hAnsi + + @rFonts_hAnsi.setter + def rFonts_hAnsi(self, value): + if value is None and self.rFonts is None: + return + rFonts = self.get_or_add_rFonts() + rFonts.hAnsi = value + + @property + def style(self): + """ + String contained in child, or None if that element is not + present. + """ + rStyle = self.rStyle + if rStyle is None: + return None + return rStyle.val + + @style.setter + def style(self, style): + """ + Set val attribute of child element to *style*, adding a + new element if necessary. If *style* is |None|, remove the + element if present. + """ + if style is None: + self._remove_rStyle() + elif self.rStyle is None: + self._add_rStyle(val=style) + else: + self.rStyle.val = style + + @property + def subscript(self): + """ + |True| if `w:vertAlign/@w:val` is 'subscript'. |False| if + `w:vertAlign/@w:val` contains any other value. |None| if + `w:vertAlign` is not present. + """ + vertAlign = self.vertAlign + if vertAlign is None: + return None + if vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT: + return True + return False + + @subscript.setter + def subscript(self, value): + if value is None: + self._remove_vertAlign() + elif bool(value) is True: + self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUBSCRIPT + elif self.vertAlign is None: + return + elif self.vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT: + self._remove_vertAlign() + + @property + def superscript(self): + """ + |True| if `w:vertAlign/@w:val` is 'superscript'. |False| if + `w:vertAlign/@w:val` contains any other value. |None| if + `w:vertAlign` is not present. + """ + vertAlign = self.vertAlign + if vertAlign is None: + return None + if vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT: + return True + return False + + @superscript.setter + def superscript(self, value): + if value is None: + self._remove_vertAlign() + elif bool(value) is True: + self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUPERSCRIPT + elif self.vertAlign is None: + return + elif self.vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT: + self._remove_vertAlign() + + @property + def sz_val(self): + """ + The value of `w:sz/@w:val` or |None| if not present. + """ + sz = self.sz + if sz is None: + return None + return sz.val + + @sz_val.setter + def sz_val(self, value): + if value is None: + self._remove_sz() + return + sz = self.get_or_add_sz() + sz.val = value + + @property + def u_val(self): + """ + Value of `w:u/@val`, or None if not present. + """ + u = self.u + if u is None: + return None + return u.val + + @u_val.setter + def u_val(self, value): + self._remove_u() + if value is not None: + self._add_u().val = value + + def _get_bool_val(self, name): + """ + Return the value of the boolean child element having *name*, e.g. + 'b', 'i', and 'smallCaps'. + """ + element = getattr(self, name) + if element is None: + return None + return element.val + + def _set_bool_val(self, name, value): + if value is None: + getattr(self, '_remove_%s' % name)() + return + element = getattr(self, 'get_or_add_%s' % name)() + element.val = value + + +class CT_Underline(BaseOxmlElement): + """ + ```` element, specifying the underlining style for a run. + """ + @property + def val(self): + """ + The underline type corresponding to the ``w:val`` attribute value. + """ + val = self.get(qn('w:val')) + underline = WD_UNDERLINE.from_xml(val) + if underline == WD_UNDERLINE.SINGLE: + return True + if underline == WD_UNDERLINE.NONE: + return False + return underline + + @val.setter + def val(self, value): + # works fine without these two mappings, but only because True == 1 + # and False == 0, which happen to match the mapping for WD_UNDERLINE + # .SINGLE and .NONE respectively. + if value is True: + value = WD_UNDERLINE.SINGLE + elif value is False: + value = WD_UNDERLINE.NONE + + val = WD_UNDERLINE.to_xml(value) + self.set(qn('w:val'), val) + + +class CT_VerticalAlignRun(BaseOxmlElement): + """ + ```` element, specifying subscript or superscript. + """ + val = RequiredAttribute('w:val', ST_VerticalAlignRun) diff --git a/build/lib/docx/oxml/text/paragraph.py b/build/lib/docx/oxml/text/paragraph.py new file mode 100644 index 000000000..36bb40e81 --- /dev/null +++ b/build/lib/docx/oxml/text/paragraph.py @@ -0,0 +1,115 @@ +# encoding: utf-8 + +""" +Custom element classes related to paragraphs (CT_P). +""" + +from ..ns import qn +from ..xmlchemy import BaseOxmlElement, OxmlElement, ZeroOrMore, ZeroOrOne + + +class CT_P(BaseOxmlElement): + """ + ```` element, containing the properties and text for a paragraph. + """ + pPr = ZeroOrOne('w:pPr') + r = ZeroOrMore('w:r') + + def _insert_pPr(self, pPr): + self.insert(0, pPr) + return pPr + + def add_p_before(self): + """ + Return a new ```` element inserted directly prior to this one. + """ + new_p = OxmlElement('w:p') + self.addprevious(new_p) + return new_p + + def link_comment(self, _id, rangeStart=0, rangeEnd=0): + rStart = OxmlElement('w:commentRangeStart') + rStart._id = _id + rEnd = OxmlElement('w:commentRangeEnd') + rEnd._id = _id + if rangeStart == 0 and rangeEnd == 0: + self.insert(0,rStart) + self.append(rEnd) + else: + self.insert(rangeStart,rStart) + if rangeEnd == len(self.getchildren() ) - 1 : + self.append(rEnd) + else: + self.insert(rangeEnd+1, rEnd) + + def add_comm(self, author, comment_part, initials, dtime, comment_text, rangeStart, rangeEnd): + + comment = comment_part.add_comment(author, initials, dtime) + comment._add_p(comment_text) + _r = self.add_r() + _r.add_comment_reference(comment._id) + self.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) + + return comment + + + + + @property + def alignment(self): + """ + The value of the ```` grandchild element or |None| if not + present. + """ + pPr = self.pPr + if pPr is None: + return None + return pPr.jc_val + + @alignment.setter + def alignment(self, value): + pPr = self.get_or_add_pPr() + pPr.jc_val = value + + def clear_content(self): + """ + Remove all child elements, except the ```` element if present. + """ + for child in self[:]: + if child.tag == qn('w:pPr'): + continue + self.remove(child) + + def set_sectPr(self, sectPr): + """ + Unconditionally replace or add *sectPr* as a grandchild in the + correct sequence. + """ + pPr = self.get_or_add_pPr() + pPr._remove_sectPr() + pPr._insert_sectPr(sectPr) + + @property + def style(self): + """ + String contained in w:val attribute of ./w:pPr/w:pStyle grandchild, + or |None| if not present. + """ + pPr = self.pPr + if pPr is None: + return None + return pPr.style + + @property + def comment_id(self): + _id = self.xpath('./w:commentRangeStart/@w:id') + if(len(_id)>1): + return None + else: + return int(_id[0]) + + + @style.setter + def style(self, style): + pPr = self.get_or_add_pPr() + pPr.style = style diff --git a/build/lib/docx/oxml/text/parfmt.py b/build/lib/docx/oxml/text/parfmt.py new file mode 100644 index 000000000..466b11b1b --- /dev/null +++ b/build/lib/docx/oxml/text/parfmt.py @@ -0,0 +1,348 @@ +# encoding: utf-8 + +""" +Custom element classes related to paragraph properties (CT_PPr). +""" + +from ...enum.text import ( + WD_ALIGN_PARAGRAPH, WD_LINE_SPACING, WD_TAB_ALIGNMENT, WD_TAB_LEADER +) +from ...shared import Length +from ..simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure +from ..xmlchemy import ( + BaseOxmlElement, OneOrMore, OptionalAttribute, RequiredAttribute, + ZeroOrOne +) + + +class CT_Ind(BaseOxmlElement): + """ + ```` element, specifying paragraph indentation. + """ + left = OptionalAttribute('w:left', ST_SignedTwipsMeasure) + right = OptionalAttribute('w:right', ST_SignedTwipsMeasure) + firstLine = OptionalAttribute('w:firstLine', ST_TwipsMeasure) + hanging = OptionalAttribute('w:hanging', ST_TwipsMeasure) + + +class CT_Jc(BaseOxmlElement): + """ + ```` element, specifying paragraph justification. + """ + val = RequiredAttribute('w:val', WD_ALIGN_PARAGRAPH) + + +class CT_PPr(BaseOxmlElement): + """ + ```` element, containing the properties for a paragraph. + """ + _tag_seq = ( + 'w:pStyle', 'w:keepNext', 'w:keepLines', 'w:pageBreakBefore', + 'w:framePr', 'w:widowControl', 'w:numPr', 'w:suppressLineNumbers', + 'w:pBdr', 'w:shd', 'w:tabs', 'w:suppressAutoHyphens', 'w:kinsoku', + 'w:wordWrap', 'w:overflowPunct', 'w:topLinePunct', 'w:autoSpaceDE', + 'w:autoSpaceDN', 'w:bidi', 'w:adjustRightInd', 'w:snapToGrid', + 'w:spacing', 'w:ind', 'w:contextualSpacing', 'w:mirrorIndents', + 'w:suppressOverlap', 'w:jc', 'w:textDirection', 'w:textAlignment', + 'w:textboxTightWrap', 'w:outlineLvl', 'w:divId', 'w:cnfStyle', + 'w:rPr', 'w:sectPr', 'w:pPrChange' + ) + pStyle = ZeroOrOne('w:pStyle', successors=_tag_seq[1:]) + keepNext = ZeroOrOne('w:keepNext', successors=_tag_seq[2:]) + keepLines = ZeroOrOne('w:keepLines', successors=_tag_seq[3:]) + pageBreakBefore = ZeroOrOne('w:pageBreakBefore', successors=_tag_seq[4:]) + widowControl = ZeroOrOne('w:widowControl', successors=_tag_seq[6:]) + numPr = ZeroOrOne('w:numPr', successors=_tag_seq[7:]) + tabs = ZeroOrOne('w:tabs', successors=_tag_seq[11:]) + spacing = ZeroOrOne('w:spacing', successors=_tag_seq[22:]) + ind = ZeroOrOne('w:ind', successors=_tag_seq[23:]) + jc = ZeroOrOne('w:jc', successors=_tag_seq[27:]) + sectPr = ZeroOrOne('w:sectPr', successors=_tag_seq[35:]) + del _tag_seq + + @property + def first_line_indent(self): + """ + A |Length| value calculated from the values of `w:ind/@w:firstLine` + and `w:ind/@w:hanging`. Returns |None| if the `w:ind` child is not + present. + """ + ind = self.ind + if ind is None: + return None + hanging = ind.hanging + if hanging is not None: + return Length(-hanging) + firstLine = ind.firstLine + if firstLine is None: + return None + return firstLine + + @first_line_indent.setter + def first_line_indent(self, value): + if self.ind is None and value is None: + return + ind = self.get_or_add_ind() + ind.firstLine = ind.hanging = None + if value is None: + return + elif value < 0: + ind.hanging = -value + else: + ind.firstLine = value + + @property + def ind_left(self): + """ + The value of `w:ind/@w:left` or |None| if not present. + """ + ind = self.ind + if ind is None: + return None + return ind.left + + @ind_left.setter + def ind_left(self, value): + if value is None and self.ind is None: + return + ind = self.get_or_add_ind() + ind.left = value + + @property + def ind_right(self): + """ + The value of `w:ind/@w:right` or |None| if not present. + """ + ind = self.ind + if ind is None: + return None + return ind.right + + @ind_right.setter + def ind_right(self, value): + if value is None and self.ind is None: + return + ind = self.get_or_add_ind() + ind.right = value + + @property + def jc_val(self): + """ + The value of the ```` child element or |None| if not present. + """ + jc = self.jc + if jc is None: + return None + return jc.val + + @jc_val.setter + def jc_val(self, value): + if value is None: + self._remove_jc() + return + self.get_or_add_jc().val = value + + @property + def keepLines_val(self): + """ + The value of `keepLines/@val` or |None| if not present. + """ + keepLines = self.keepLines + if keepLines is None: + return None + return keepLines.val + + @keepLines_val.setter + def keepLines_val(self, value): + if value is None: + self._remove_keepLines() + else: + self.get_or_add_keepLines().val = value + + @property + def keepNext_val(self): + """ + The value of `keepNext/@val` or |None| if not present. + """ + keepNext = self.keepNext + if keepNext is None: + return None + return keepNext.val + + @keepNext_val.setter + def keepNext_val(self, value): + if value is None: + self._remove_keepNext() + else: + self.get_or_add_keepNext().val = value + + @property + def pageBreakBefore_val(self): + """ + The value of `pageBreakBefore/@val` or |None| if not present. + """ + pageBreakBefore = self.pageBreakBefore + if pageBreakBefore is None: + return None + return pageBreakBefore.val + + @pageBreakBefore_val.setter + def pageBreakBefore_val(self, value): + if value is None: + self._remove_pageBreakBefore() + else: + self.get_or_add_pageBreakBefore().val = value + + @property + def spacing_after(self): + """ + The value of `w:spacing/@w:after` or |None| if not present. + """ + spacing = self.spacing + if spacing is None: + return None + return spacing.after + + @spacing_after.setter + def spacing_after(self, value): + if value is None and self.spacing is None: + return + self.get_or_add_spacing().after = value + + @property + def spacing_before(self): + """ + The value of `w:spacing/@w:before` or |None| if not present. + """ + spacing = self.spacing + if spacing is None: + return None + return spacing.before + + @spacing_before.setter + def spacing_before(self, value): + if value is None and self.spacing is None: + return + self.get_or_add_spacing().before = value + + @property + def spacing_line(self): + """ + The value of `w:spacing/@w:line` or |None| if not present. + """ + spacing = self.spacing + if spacing is None: + return None + return spacing.line + + @spacing_line.setter + def spacing_line(self, value): + if value is None and self.spacing is None: + return + self.get_or_add_spacing().line = value + + @property + def spacing_lineRule(self): + """ + The value of `w:spacing/@w:lineRule` as a member of the + :ref:`WdLineSpacing` enumeration. Only the `MULTIPLE`, `EXACTLY`, and + `AT_LEAST` members are used. It is the responsibility of the client + to calculate the use of `SINGLE`, `DOUBLE`, and `MULTIPLE` based on + the value of `w:spacing/@w:line` if that behavior is desired. + """ + spacing = self.spacing + if spacing is None: + return None + lineRule = spacing.lineRule + if lineRule is None and spacing.line is not None: + return WD_LINE_SPACING.MULTIPLE + return lineRule + + @spacing_lineRule.setter + def spacing_lineRule(self, value): + if value is None and self.spacing is None: + return + self.get_or_add_spacing().lineRule = value + + @property + def style(self): + """ + String contained in child, or None if that element is not + present. + """ + pStyle = self.pStyle + if pStyle is None: + return None + return pStyle.val + + @style.setter + def style(self, style): + """ + Set val attribute of child element to *style*, adding a + new element if necessary. If *style* is |None|, remove the + element if present. + """ + if style is None: + self._remove_pStyle() + return + pStyle = self.get_or_add_pStyle() + pStyle.val = style + + @property + def widowControl_val(self): + """ + The value of `widowControl/@val` or |None| if not present. + """ + widowControl = self.widowControl + if widowControl is None: + return None + return widowControl.val + + @widowControl_val.setter + def widowControl_val(self, value): + if value is None: + self._remove_widowControl() + else: + self.get_or_add_widowControl().val = value + + +class CT_Spacing(BaseOxmlElement): + """ + ```` element, specifying paragraph spacing attributes such as + space before and line spacing. + """ + after = OptionalAttribute('w:after', ST_TwipsMeasure) + before = OptionalAttribute('w:before', ST_TwipsMeasure) + line = OptionalAttribute('w:line', ST_SignedTwipsMeasure) + lineRule = OptionalAttribute('w:lineRule', WD_LINE_SPACING) + + +class CT_TabStop(BaseOxmlElement): + """ + ```` element, representing an individual tab stop. + """ + val = RequiredAttribute('w:val', WD_TAB_ALIGNMENT) + leader = OptionalAttribute( + 'w:leader', WD_TAB_LEADER, default=WD_TAB_LEADER.SPACES + ) + pos = RequiredAttribute('w:pos', ST_SignedTwipsMeasure) + + +class CT_TabStops(BaseOxmlElement): + """ + ```` element, container for a sorted sequence of tab stops. + """ + tab = OneOrMore('w:tab', successors=()) + + def insert_tab_in_order(self, pos, align, leader): + """ + Insert a newly created `w:tab` child element in *pos* order. + """ + new_tab = self._new_tab() + new_tab.pos, new_tab.val, new_tab.leader = pos, align, leader + for tab in self.tab_lst: + if new_tab.pos < tab.pos: + tab.addprevious(new_tab) + return new_tab + self.append(new_tab) + return new_tab diff --git a/build/lib/docx/oxml/text/run.py b/build/lib/docx/oxml/text/run.py new file mode 100644 index 000000000..010eb2935 --- /dev/null +++ b/build/lib/docx/oxml/text/run.py @@ -0,0 +1,183 @@ +# encoding: utf-8 + +""" +Custom element classes related to text runs (CT_R). +""" + +from ..ns import qn +from ..simpletypes import ST_BrClear, ST_BrType +from .. import OxmlElement +from ..xmlchemy import ( + BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne +) + +from .. import OxmlElement + + +class CT_Br(BaseOxmlElement): + """ + ```` element, indicating a line, page, or column break in a run. + """ + type = OptionalAttribute('w:type', ST_BrType) + clear = OptionalAttribute('w:clear', ST_BrClear) + + +class CT_R(BaseOxmlElement): + """ + ```` element, containing the properties and text for a run. + """ + rPr = ZeroOrOne('w:rPr') + ###wrong + ref = ZeroOrOne('w:commentRangeStart', successors=('w:r',)) + t = ZeroOrMore('w:t') + br = ZeroOrMore('w:br') + cr = ZeroOrMore('w:cr') + tab = ZeroOrMore('w:tab') + drawing = ZeroOrMore('w:drawing') + + def _insert_rPr(self, rPr): + self.insert(0, rPr) + return rPr + + def add_t(self, text): + """ + Return a newly added ```` element containing *text*. + """ + t = self._add_t(text=text) + if len(text.strip()) < len(text): + t.set(qn('xml:space'), 'preserve') + return t + + def add_drawing(self, inline_or_anchor): + """ + Return a newly appended ``CT_Drawing`` (````) child + element having *inline_or_anchor* as its child. + """ + drawing = self._add_drawing() + drawing.append(inline_or_anchor) + return drawing + + def add_comment_reference(self, _id): + reference = OxmlElement('w:commentReference') + reference._id = _id + self.append(reference) + return reference + + def clear_content(self): + """ + Remove all child elements except the ```` element if present. + """ + content_child_elms = self[1:] if self.rPr is not None else self[:] + for child in content_child_elms: + self.remove(child) + + def add_comment_reference(self, _id): + reference = OxmlElement('w:commentReference') + reference._id = _id + self.append(reference) + return reference + + @property + def style(self): + """ + String contained in w:val attribute of grandchild, or + |None| if that element is not present. + """ + rPr = self.rPr + if rPr is None: + return None + return rPr.style + + @style.setter + def style(self, style): + """ + Set the character style of this element to *style*. If *style* + is None, remove the style element. + """ + rPr = self.get_or_add_rPr() + rPr.style = style + + @property + def text(self): + """ + A string representing the textual content of this run, with content + child elements like ```` translated to their Python + equivalent. + """ + text = '' + for child in self: + if child.tag == qn('w:t'): + t_text = child.text + text += t_text if t_text is not None else '' + elif child.tag == qn('w:tab'): + text += '\t' + elif child.tag in (qn('w:br'), qn('w:cr')): + text += '\n' + return text + + @text.setter + def text(self, text): + self.clear_content() + _RunContentAppender.append_to_run_from_text(self, text) + + +class CT_Text(BaseOxmlElement): + """ + ```` element, containing a sequence of characters within a run. + """ + + +class _RunContentAppender(object): + """ + Service object that knows how to translate a Python string into run + content elements appended to a specified ```` element. Contiguous + sequences of regular characters are appended in a single ```` + element. Each tab character ('\t') causes a ```` element to be + appended. Likewise a newline or carriage return character ('\n', '\r') + causes a ```` element to be appended. + """ + def __init__(self, r): + self._r = r + self._bfr = [] + + @classmethod + def append_to_run_from_text(cls, r, text): + """ + Create a "one-shot" ``_RunContentAppender`` instance and use it to + append the run content elements corresponding to *text* to the + ```` element *r*. + """ + appender = cls(r) + appender.add_text(text) + + def add_text(self, text): + """ + Append the run content elements corresponding to *text* to the + ```` element of this instance. + """ + for char in text: + self.add_char(char) + self.flush() + + def add_char(self, char): + """ + Process the next character of input through the translation finite + state maching (FSM). There are two possible states, buffer pending + and not pending, but those are hidden behind the ``.flush()`` method + which must be called at the end of text to ensure any pending + ```` element is written. + """ + if char == '\t': + self.flush() + self._r.add_tab() + elif char in '\r\n': + self.flush() + self._r.add_br() + else: + self._bfr.append(char) + + def flush(self): + text = ''.join(self._bfr) + if text: + self._r.add_t(text) + del self._bfr[:] diff --git a/build/lib/docx/oxml/xmlchemy.py b/build/lib/docx/oxml/xmlchemy.py new file mode 100644 index 000000000..40df33494 --- /dev/null +++ b/build/lib/docx/oxml/xmlchemy.py @@ -0,0 +1,761 @@ +# encoding: utf-8 + +""" +Provides a wrapper around lxml that enables declarative definition of custom +element classes. +""" + +from __future__ import absolute_import + +from lxml import etree + +import re + +from . import OxmlElement +from ..compat import Unicode +from .exceptions import InvalidXmlError +from .ns import NamespacePrefixedTag, nsmap, qn +from ..shared import lazyproperty + + +def serialize_for_reading(element): + """ + Serialize *element* to human-readable XML suitable for tests. No XML + declaration. + """ + xml = etree.tostring(element, encoding='unicode', pretty_print=True) + return XmlString(xml) + + +class XmlString(Unicode): + """ + Provides string comparison override suitable for serialized XML that is + useful for tests. + """ + + # ' text' + # | | || | + # +----------+------------------------------------------++-----------+ + # front attrs | text + # close + + _xml_elm_line_patt = re.compile( + '( *)([^<]*)?$' + ) + + def __eq__(self, other): + lines = self.splitlines() + lines_other = other.splitlines() + if len(lines) != len(lines_other): + return False + for line, line_other in zip(lines, lines_other): + if not self._eq_elm_strs(line, line_other): + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def _attr_seq(self, attrs): + """ + Return a sequence of attribute strings parsed from *attrs*. Each + attribute string is stripped of whitespace on both ends. + """ + attrs = attrs.strip() + attr_lst = attrs.split() + return sorted(attr_lst) + + def _eq_elm_strs(self, line, line_2): + """ + Return True if the element in *line_2* is XML equivalent to the + element in *line*. + """ + front, attrs, close, text = self._parse_line(line) + front_2, attrs_2, close_2, text_2 = self._parse_line(line_2) + if front != front_2: + return False + if self._attr_seq(attrs) != self._attr_seq(attrs_2): + return False + if close != close_2: + return False + if text != text_2: + return False + return True + + @classmethod + def _parse_line(cls, line): + """ + Return front, attrs, close, text 4-tuple result of parsing XML element + string *line*. + """ + match = cls._xml_elm_line_patt.match(line) + front, attrs, close, text = [match.group(n) for n in range(1, 5)] + return front, attrs, close, text + + +class MetaOxmlElement(type): + """ + Metaclass for BaseOxmlElement + """ + def __init__(cls, clsname, bases, clsdict): + dispatchable = ( + OneAndOnlyOne, OneOrMore, OptionalAttribute, RequiredAttribute, + ZeroOrMore, ZeroOrOne, ZeroOrOneChoice + ) + for key, value in clsdict.items(): + if isinstance(value, dispatchable): + value.populate_class_members(cls, key) + + +class BaseAttribute(object): + """ + Base class for OptionalAttribute and RequiredAttribute, providing common + methods. + """ + def __init__(self, attr_name, simple_type): + super(BaseAttribute, self).__init__() + self._attr_name = attr_name + self._simple_type = simple_type + + def populate_class_members(self, element_cls, prop_name): + """ + Add the appropriate methods to *element_cls*. + """ + self._element_cls = element_cls + self._prop_name = prop_name + + self._add_attr_property() + + def _add_attr_property(self): + """ + Add a read/write ``{prop_name}`` property to the element class that + returns the interpreted value of this attribute on access and changes + the attribute value to its ST_* counterpart on assignment. + """ + property_ = property(self._getter, self._setter, None) + # assign unconditionally to overwrite element name definition + setattr(self._element_cls, self._prop_name, property_) + + @property + def _clark_name(self): + if ':' in self._attr_name: + return qn(self._attr_name) + return self._attr_name + + +class OptionalAttribute(BaseAttribute): + """ + Defines an optional attribute on a custom element class. An optional + attribute returns a default value when not present for reading. When + assigned |None|, the attribute is removed. + """ + def __init__(self, attr_name, simple_type, default=None): + super(OptionalAttribute, self).__init__(attr_name, simple_type) + self._default = default + + @property + def _getter(self): + """ + Return a function object suitable for the "get" side of the attribute + property descriptor. + """ + def get_attr_value(obj): + attr_str_value = obj.get(self._clark_name) + if attr_str_value is None: + return self._default + return self._simple_type.from_xml(attr_str_value) + get_attr_value.__doc__ = self._docstring + return get_attr_value + + @property + def _docstring(self): + """ + Return the string to use as the ``__doc__`` attribute of the property + for this attribute. + """ + return ( + '%s type-converted value of ``%s`` attribute, or |None| (or spec' + 'ified default value) if not present. Assigning the default valu' + 'e causes the attribute to be removed from the element.' % + (self._simple_type.__name__, self._attr_name) + ) + + @property + def _setter(self): + """ + Return a function object suitable for the "set" side of the attribute + property descriptor. + """ + def set_attr_value(obj, value): + if value is None or value == self._default: + if self._clark_name in obj.attrib: + del obj.attrib[self._clark_name] + return + str_value = self._simple_type.to_xml(value) + obj.set(self._clark_name, str_value) + return set_attr_value + + +class RequiredAttribute(BaseAttribute): + """ + Defines a required attribute on a custom element class. A required + attribute is assumed to be present for reading, so does not have + a default value; its actual value is always used. If missing on read, + an |InvalidXmlError| is raised. It also does not remove the attribute if + |None| is assigned. Assigning |None| raises |TypeError| or |ValueError|, + depending on the simple type of the attribute. + """ + @property + def _getter(self): + """ + Return a function object suitable for the "get" side of the attribute + property descriptor. + """ + def get_attr_value(obj): + attr_str_value = obj.get(self._clark_name) + if attr_str_value is None: + raise InvalidXmlError( + "required '%s' attribute not present on element %s" % + (self._attr_name, obj.tag) + ) + return self._simple_type.from_xml(attr_str_value) + get_attr_value.__doc__ = self._docstring + return get_attr_value + + @property + def _docstring(self): + """ + Return the string to use as the ``__doc__`` attribute of the property + for this attribute. + """ + return ( + '%s type-converted value of ``%s`` attribute.' % + (self._simple_type.__name__, self._attr_name) + ) + + @property + def _setter(self): + """ + Return a function object suitable for the "set" side of the attribute + property descriptor. + """ + def set_attr_value(obj, value): + str_value = self._simple_type.to_xml(value) + obj.set(self._clark_name, str_value) + return set_attr_value + + +class _BaseChildElement(object): + """ + Base class for the child element classes corresponding to varying + cardinalities, such as ZeroOrOne and ZeroOrMore. + """ + def __init__(self, nsptagname, successors=()): + super(_BaseChildElement, self).__init__() + self._nsptagname = nsptagname + self._successors = successors + + def populate_class_members(self, element_cls, prop_name): + """ + Baseline behavior for adding the appropriate methods to + *element_cls*. + """ + self._element_cls = element_cls + self._prop_name = prop_name + + def _add_adder(self): + """ + Add an ``_add_x()`` method to the element class for this child + element. + """ + def _add_child(obj, **attrs): + new_method = getattr(obj, self._new_method_name) + child = new_method() + for key, value in attrs.items(): + setattr(child, key, value) + insert_method = getattr(obj, self._insert_method_name) + insert_method(child) + return child + + _add_child.__doc__ = ( + 'Add a new ``<%s>`` child element unconditionally, inserted in t' + 'he correct sequence.' % self._nsptagname + ) + self._add_to_class(self._add_method_name, _add_child) + + def _add_creator(self): + """ + Add a ``_new_{prop_name}()`` method to the element class that creates + a new, empty element of the correct type, having no attributes. + """ + creator = self._creator + creator.__doc__ = ( + 'Return a "loose", newly created ``<%s>`` element having no attri' + 'butes, text, or children.' % self._nsptagname + ) + self._add_to_class(self._new_method_name, creator) + + def _add_getter(self): + """ + Add a read-only ``{prop_name}`` property to the element class for + this child element. + """ + property_ = property(self._getter, None, None) + # assign unconditionally to overwrite element name definition + setattr(self._element_cls, self._prop_name, property_) + + def _add_inserter(self): + """ + Add an ``_insert_x()`` method to the element class for this child + element. + """ + def _insert_child(obj, child): + obj.insert_element_before(child, *self._successors) + return child + + _insert_child.__doc__ = ( + 'Return the passed ``<%s>`` element after inserting it as a chil' + 'd in the correct sequence.' % self._nsptagname + ) + self._add_to_class(self._insert_method_name, _insert_child) + + def _add_list_getter(self): + """ + Add a read-only ``{prop_name}_lst`` property to the element class to + retrieve a list of child elements matching this type. + """ + prop_name = '%s_lst' % self._prop_name + property_ = property(self._list_getter, None, None) + setattr(self._element_cls, prop_name, property_) + + @lazyproperty + def _add_method_name(self): + return '_add_%s' % self._prop_name + + def _add_public_adder(self): + """ + Add a public ``add_x()`` method to the parent element class. + """ + def add_child(obj): + private_add_method = getattr(obj, self._add_method_name) + child = private_add_method() + return child + + add_child.__doc__ = ( + 'Add a new ``<%s>`` child element unconditionally, inserted in t' + 'he correct sequence.' % self._nsptagname + ) + self._add_to_class(self._public_add_method_name, add_child) + + def _add_to_class(self, name, method): + """ + Add *method* to the target class as *name*, unless *name* is already + defined on the class. + """ + if hasattr(self._element_cls, name): + return + setattr(self._element_cls, name, method) + + @property + def _creator(self): + """ + Return a function object that creates a new, empty element of the + right type, having no attributes. + """ + def new_child_element(obj): + return OxmlElement(self._nsptagname) + return new_child_element + + @property + def _getter(self): + """ + Return a function object suitable for the "get" side of the property + descriptor. This default getter returns the child element with + matching tag name or |None| if not present. + """ + def get_child_element(obj): + return obj.find(qn(self._nsptagname)) + get_child_element.__doc__ = ( + '``<%s>`` child element or |None| if not present.' + % self._nsptagname + ) + return get_child_element + + @lazyproperty + def _insert_method_name(self): + return '_insert_%s' % self._prop_name + + @property + def _list_getter(self): + """ + Return a function object suitable for the "get" side of a list + property descriptor. + """ + def get_child_element_list(obj): + return obj.findall(qn(self._nsptagname)) + get_child_element_list.__doc__ = ( + 'A list containing each of the ``<%s>`` child elements, in the o' + 'rder they appear.' % self._nsptagname + ) + return get_child_element_list + + @lazyproperty + def _public_add_method_name(self): + """ + add_childElement() is public API for a repeating element, allowing + new elements to be added to the sequence. May be overridden to + provide a friendlier API to clients having domain appropriate + parameter names for required attributes. + """ + return 'add_%s' % self._prop_name + + @lazyproperty + def _remove_method_name(self): + return '_remove_%s' % self._prop_name + + @lazyproperty + def _new_method_name(self): + return '_new_%s' % self._prop_name + + +class Choice(_BaseChildElement): + """ + Defines a child element belonging to a group, only one of which may + appear as a child. + """ + @property + def nsptagname(self): + return self._nsptagname + + def populate_class_members( + self, element_cls, group_prop_name, successors): + """ + Add the appropriate methods to *element_cls*. + """ + self._element_cls = element_cls + self._group_prop_name = group_prop_name + self._successors = successors + + self._add_getter() + self._add_creator() + self._add_inserter() + self._add_adder() + self._add_get_or_change_to_method() + + def _add_get_or_change_to_method(self): + """ + Add a ``get_or_change_to_x()`` method to the element class for this + child element. + """ + def get_or_change_to_child(obj): + child = getattr(obj, self._prop_name) + if child is not None: + return child + remove_group_method = getattr( + obj, self._remove_group_method_name + ) + remove_group_method() + add_method = getattr(obj, self._add_method_name) + child = add_method() + return child + + get_or_change_to_child.__doc__ = ( + 'Return the ``<%s>`` child, replacing any other group element if' + ' found.' + ) % self._nsptagname + self._add_to_class( + self._get_or_change_to_method_name, get_or_change_to_child + ) + + @property + def _prop_name(self): + """ + Calculate property name from tag name, e.g. a:schemeClr -> schemeClr. + """ + if ':' in self._nsptagname: + start = self._nsptagname.index(':')+1 + else: + start = 0 + return self._nsptagname[start:] + + @lazyproperty + def _get_or_change_to_method_name(self): + return 'get_or_change_to_%s' % self._prop_name + + @lazyproperty + def _remove_group_method_name(self): + return '_remove_%s' % self._group_prop_name + + +class OneAndOnlyOne(_BaseChildElement): + """ + Defines a required child element for MetaOxmlElement. + """ + def __init__(self, nsptagname): + super(OneAndOnlyOne, self).__init__(nsptagname, None) + + def populate_class_members(self, element_cls, prop_name): + """ + Add the appropriate methods to *element_cls*. + """ + super(OneAndOnlyOne, self).populate_class_members( + element_cls, prop_name + ) + self._add_getter() + + @property + def _getter(self): + """ + Return a function object suitable for the "get" side of the property + descriptor. + """ + def get_child_element(obj): + child = obj.find(qn(self._nsptagname)) + if child is None: + raise InvalidXmlError( + "required ``<%s>`` child element not present" % + self._nsptagname + ) + return child + + get_child_element.__doc__ = ( + 'Required ``<%s>`` child element.' + % self._nsptagname + ) + return get_child_element + + +class OneOrMore(_BaseChildElement): + """ + Defines a repeating child element for MetaOxmlElement that must appear at + least once. + """ + def populate_class_members(self, element_cls, prop_name): + """ + Add the appropriate methods to *element_cls*. + """ + super(OneOrMore, self).populate_class_members( + element_cls, prop_name + ) + self._add_list_getter() + self._add_creator() + self._add_inserter() + self._add_adder() + self._add_public_adder() + delattr(element_cls, prop_name) + + +class ZeroOrMore(_BaseChildElement): + """ + Defines an optional repeating child element for MetaOxmlElement. + """ + def populate_class_members(self, element_cls, prop_name): + """ + Add the appropriate methods to *element_cls*. + """ + super(ZeroOrMore, self).populate_class_members( + element_cls, prop_name + ) + self._add_list_getter() + self._add_creator() + self._add_inserter() + self._add_adder() + self._add_public_adder() + delattr(element_cls, prop_name) + + +class ZeroOrOne(_BaseChildElement): + """ + Defines an optional child element for MetaOxmlElement. + """ + def populate_class_members(self, element_cls, prop_name): + """ + Add the appropriate methods to *element_cls*. + """ + super(ZeroOrOne, self).populate_class_members(element_cls, prop_name) + self._add_getter() + self._add_creator() + self._add_inserter() + self._add_adder() + self._add_get_or_adder() + self._add_remover() + + def _add_get_or_adder(self): + """ + Add a ``get_or_add_x()`` method to the element class for this + child element. + """ + def get_or_add_child(obj): + child = getattr(obj, self._prop_name) + if child is None: + add_method = getattr(obj, self._add_method_name) + child = add_method() + return child + get_or_add_child.__doc__ = ( + 'Return the ``<%s>`` child element, newly added if not present.' + ) % self._nsptagname + self._add_to_class(self._get_or_add_method_name, get_or_add_child) + + def _add_remover(self): + """ + Add a ``_remove_x()`` method to the element class for this child + element. + """ + def _remove_child(obj): + obj.remove_all(self._nsptagname) + _remove_child.__doc__ = ( + 'Remove all ``<%s>`` child elements.' + ) % self._nsptagname + self._add_to_class(self._remove_method_name, _remove_child) + + @lazyproperty + def _get_or_add_method_name(self): + return 'get_or_add_%s' % self._prop_name + + +class ZeroOrOneChoice(_BaseChildElement): + """ + Correspondes to an ``EG_*`` element group where at most one of its + members may appear as a child. + """ + def __init__(self, choices, successors=()): + self._choices = choices + self._successors = successors + + def populate_class_members(self, element_cls, prop_name): + """ + Add the appropriate methods to *element_cls*. + """ + super(ZeroOrOneChoice, self).populate_class_members( + element_cls, prop_name + ) + self._add_choice_getter() + for choice in self._choices: + choice.populate_class_members( + element_cls, self._prop_name, self._successors + ) + self._add_group_remover() + + def _add_choice_getter(self): + """ + Add a read-only ``{prop_name}`` property to the element class that + returns the present member of this group, or |None| if none are + present. + """ + property_ = property(self._choice_getter, None, None) + # assign unconditionally to overwrite element name definition + setattr(self._element_cls, self._prop_name, property_) + + def _add_group_remover(self): + """ + Add a ``_remove_eg_x()`` method to the element class for this choice + group. + """ + def _remove_choice_group(obj): + for tagname in self._member_nsptagnames: + obj.remove_all(tagname) + + _remove_choice_group.__doc__ = ( + 'Remove the current choice group child element if present.' + ) + self._add_to_class( + self._remove_choice_group_method_name, _remove_choice_group + ) + + @property + def _choice_getter(self): + """ + Return a function object suitable for the "get" side of the property + descriptor. + """ + def get_group_member_element(obj): + return obj.first_child_found_in(*self._member_nsptagnames) + get_group_member_element.__doc__ = ( + 'Return the child element belonging to this element group, or ' + '|None| if no member child is present.' + ) + return get_group_member_element + + @lazyproperty + def _member_nsptagnames(self): + """ + Sequence of namespace-prefixed tagnames, one for each of the member + elements of this choice group. + """ + return [choice.nsptagname for choice in self._choices] + + @lazyproperty + def _remove_choice_group_method_name(self): + return '_remove_%s' % self._prop_name + + +class _OxmlElementBase(etree.ElementBase): + """ + Effective base class for all custom element classes, to add standardized + behavior to all classes in one place. Actual inheritance is from + BaseOxmlElement below, needed to manage Python 2-3 metaclass declaration + compatibility. + """ + + __metaclass__ = MetaOxmlElement + + def __repr__(self): + return "<%s '<%s>' at 0x%0x>" % ( + self.__class__.__name__, self._nsptag, id(self) + ) + + def first_child_found_in(self, *tagnames): + """ + Return the first child found with tag in *tagnames*, or None if + not found. + """ + for tagname in tagnames: + child = self.find(qn(tagname)) + if child is not None: + return child + return None + + def insert_element_before(self, elm, *tagnames): + successor = self.first_child_found_in(*tagnames) + if successor is not None: + successor.addprevious(elm) + else: + self.append(elm) + return elm + + def remove_all(self, *tagnames): + """ + Remove all child elements whose tagname (e.g. 'a:p') appears in + *tagnames*. + """ + for tagname in tagnames: + matching = self.findall(qn(tagname)) + for child in matching: + self.remove(child) + + @property + def xml(self): + """ + Return XML string for this element, suitable for testing purposes. + Pretty printed for readability and without an XML declaration at the + top. + """ + return serialize_for_reading(self) + + def xpath(self, xpath_str): + """ + Override of ``lxml`` _Element.xpath() method to provide standard Open + XML namespace mapping (``nsmap``) in centralized location. + """ + return super(BaseOxmlElement, self).xpath( + xpath_str, namespaces=nsmap + ) + + @property + def _nsptag(self): + return NamespacePrefixedTag.from_clark_name(self.tag) + + +BaseOxmlElement = MetaOxmlElement( + 'BaseOxmlElement', (etree.ElementBase,), dict(_OxmlElementBase.__dict__) +) diff --git a/build/lib/docx/package.py b/build/lib/docx/package.py new file mode 100644 index 000000000..4c9a6f6a1 --- /dev/null +++ b/build/lib/docx/package.py @@ -0,0 +1,115 @@ +# encoding: utf-8 + +""" +WordprocessingML Package class and related objects +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from docx.image.image import Image +from docx.opc.constants import RELATIONSHIP_TYPE as RT +from docx.opc.package import OpcPackage +from docx.opc.packuri import PackURI +from docx.parts.image import ImagePart +from docx.shared import lazyproperty + + +class Package(OpcPackage): + """ + Customizations specific to a WordprocessingML package. + """ + def after_unmarshal(self): + """ + Called by loading code after all parts and relationships have been + loaded, to afford the opportunity for any required post-processing. + """ + self._gather_image_parts() + + @lazyproperty + def image_parts(self): + """ + Collection of all image parts in this package. + """ + return ImageParts() + + def _gather_image_parts(self): + """ + Load the image part collection with all the image parts in package. + """ + for rel in self.iter_rels(): + if rel.is_external: + continue + if rel.reltype != RT.IMAGE: + continue + if rel.target_part in self.image_parts: + continue + self.image_parts.append(rel.target_part) + + +class ImageParts(object): + """ + Collection of |ImagePart| instances corresponding to each image part in + the package. + """ + def __init__(self): + super(ImageParts, self).__init__() + self._image_parts = [] + + def __contains__(self, item): + return self._image_parts.__contains__(item) + + def __iter__(self): + return self._image_parts.__iter__() + + def __len__(self): + return self._image_parts.__len__() + + def append(self, item): + self._image_parts.append(item) + + def get_or_add_image_part(self, image_descriptor): + """ + Return an |ImagePart| instance containing the image identified by + *image_descriptor*, newly created if a matching one is not present in + the collection. + """ + image = Image.from_file(image_descriptor) + matching_image_part = self._get_by_sha1(image.sha1) + if matching_image_part is not None: + return matching_image_part + return self._add_image_part(image) + + def _add_image_part(self, image): + """ + Return an |ImagePart| instance newly created from image and appended + to the collection. + """ + partname = self._next_image_partname(image.ext) + image_part = ImagePart.from_image(image, partname) + self.append(image_part) + return image_part + + def _get_by_sha1(self, sha1): + """ + Return the image part in this collection having a SHA1 hash matching + *sha1*, or |None| if not found. + """ + for image_part in self._image_parts: + if image_part.sha1 == sha1: + return image_part + return None + + def _next_image_partname(self, ext): + """ + The next available image partname, starting from + ``/word/media/image1.{ext}`` where unused numbers are reused. The + partname is unique by number, without regard to the extension. *ext* + does not include the leading period. + """ + def image_partname(n): + return PackURI('/word/media/image%d.%s' % (n, ext)) + used_numbers = [image_part.partname.idx for image_part in self] + for n in range(1, len(self)+1): + if n not in used_numbers: + return image_partname(n) + return image_partname(len(self)+1) diff --git a/build/lib/docx/parts/__init__.py b/build/lib/docx/parts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/build/lib/docx/parts/comments.py b/build/lib/docx/parts/comments.py new file mode 100644 index 000000000..03a045aea --- /dev/null +++ b/build/lib/docx/parts/comments.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +import os + +from docx.opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI + +from docx.oxml import parse_xml +from ..opc.part import XmlPart + +class CommentsPart(XmlPart): + """Definition of Comments Part""" + + @classmethod + def default(cls, package): + partname = PackURI("/word/comments.xml") + content_type = CT.WML_COMMENTS + element = parse_xml(cls._default_comments_xml()) + return cls(partname, content_type, element, package) + + @classmethod + def _default_comments_xml(cls): + path = os.path.join(os.path.split(__file__)[0], '..', 'templates', 'default-comments.xml') + with open(path, 'rb') as f: + xml_bytes = f.read() + return xml_bytes diff --git a/build/lib/docx/parts/document.py b/build/lib/docx/parts/document.py new file mode 100644 index 000000000..a10b1e690 --- /dev/null +++ b/build/lib/docx/parts/document.py @@ -0,0 +1,192 @@ +# encoding: utf-8 + +""" +|DocumentPart| and closely related objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..document import Document +from .numbering import NumberingPart +from ..opc.constants import RELATIONSHIP_TYPE as RT +from ..opc.part import XmlPart +from ..oxml.shape import CT_Inline +from ..shape import InlineShapes +from ..shared import lazyproperty +from .settings import SettingsPart +from .styles import StylesPart +from .comments import CommentsPart + + +class DocumentPart(XmlPart): + """ + Main document part of a WordprocessingML (WML) package, aka a .docx file. + Acts as broker to other parts such as image, core properties, and style + parts. It also acts as a convenient delegate when a mid-document object + needs a service involving a remote ancestor. The `Parented.part` property + inherited by many content objects provides access to this part object for + that purpose. + """ + @property + def core_properties(self): + """ + A |CoreProperties| object providing read/write access to the core + properties of this document. + """ + return self.package.core_properties + + @property + def document(self): + """ + A |Document| object providing access to the content of this document. + """ + return Document(self._element, self) + + def get_or_add_image(self, image_descriptor): + """ + Return an (rId, image) 2-tuple for the image identified by + *image_descriptor*. *image* is an |Image| instance providing access + to the properties of the image, such as dimensions and image type. + *rId* is the key for the relationship between this document part and + the image part, reused if already present, newly created if not. + """ + image_part = self._package.image_parts.get_or_add_image_part( + image_descriptor + ) + rId = self.relate_to(image_part, RT.IMAGE) + return rId, image_part.image + + def get_style(self, style_id, style_type): + """ + Return the style in this document matching *style_id*. Returns the + default style for *style_type* if *style_id* is |None| or does not + match a defined style of *style_type*. + """ + return self.styles.get_by_id(style_id, style_type) + + def get_style_id(self, style_or_name, style_type): + """ + Return the style_id (|str|) of the style of *style_type* matching + *style_or_name*. Returns |None| if the style resolves to the default + style for *style_type* or if *style_or_name* is itself |None|. Raises + if *style_or_name* is a style of the wrong type or names a style not + present in the document. + """ + return self.styles.get_style_id(style_or_name, style_type) + + @lazyproperty + def inline_shapes(self): + """ + The |InlineShapes| instance containing the inline shapes in the + document. + """ + return InlineShapes(self._element.body, self) + + def new_pic_inline(self, image_descriptor, width, height): + """ + Return a newly-created `w:inline` element containing the image + specified by *image_descriptor* and scaled based on the values of + *width* and *height*. + """ + rId, image = self.get_or_add_image(image_descriptor) + cx, cy = image.scaled_dimensions(width, height) + shape_id, filename = self.next_id, image.filename + return CT_Inline.new_pic_inline(shape_id, rId, filename, cx, cy) + + @property + def next_id(self): + """Next available positive integer id value in this document. + + Calculated by incrementing maximum existing id value. Gaps in the + existing id sequence are not filled. The id attribute value is unique + in the document, without regard to the element type it appears on. + """ + id_str_lst = self._element.xpath('//@id') + used_ids = [int(id_str) for id_str in id_str_lst if id_str.isdigit()] + if not used_ids: + return 1 + return max(used_ids) + 1 + + @lazyproperty + def numbering_part(self): + """ + A |NumberingPart| object providing access to the numbering + definitions for this document. Creates an empty numbering part if one + is not present. + """ + try: + return self.part_related_by(RT.NUMBERING) + except KeyError: + numbering_part = NumberingPart.new() + self.relate_to(numbering_part, RT.NUMBERING) + return numbering_part + + + + def save(self, path_or_stream): + """ + Save this document to *path_or_stream*, which can be either a path to + a filesystem location (a string) or a file-like object. + """ + self.package.save(path_or_stream) + + @property + def settings(self): + """ + A |Settings| object providing access to the settings in the settings + part of this document. + """ + return self._settings_part.settings + + @property + def styles(self): + """ + A |Styles| object providing access to the styles in the styles part + of this document. + """ + return self._styles_part.styles + + @property + def _settings_part(self): + """ + A |SettingsPart| object providing access to the document-level + settings for this document. Creates a default settings part if one is + not present. + """ + try: + return self.part_related_by(RT.SETTINGS) + except KeyError: + settings_part = SettingsPart.default(self.package) + self.relate_to(settings_part, RT.SETTINGS) + return settings_part + + @property + def _styles_part(self): + """ + Instance of |StylesPart| for this document. Creates an empty styles + part if one is not present. + """ + try: + return self.part_related_by(RT.STYLES) + except KeyError: + styles_part = StylesPart.default(self.package) + self.relate_to(styles_part, RT.STYLES) + return styles_part + @lazyproperty + def comments_part(self): + """ + A |Comments| object providing read/write access to the core + properties of this document. + """ + # return self.package._comments_part + + @property + def _comments_part(self): + try: + return self.part_related_by(RT.COMMENTS) + except KeyError: + comments_part = CommentsPart.default(self) + self.relate_to(comments_part, RT.COMMENTS) + return comments_part \ No newline at end of file diff --git a/build/lib/docx/parts/image.py b/build/lib/docx/parts/image.py new file mode 100644 index 000000000..6ece20d80 --- /dev/null +++ b/build/lib/docx/parts/image.py @@ -0,0 +1,89 @@ +# encoding: utf-8 + +""" +The proxy class for an image part, and related objects. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import hashlib + +from docx.image.image import Image +from docx.opc.part import Part +from docx.shared import Emu, Inches + + +class ImagePart(Part): + """ + An image part. Corresponds to the target part of a relationship with type + RELATIONSHIP_TYPE.IMAGE. + """ + def __init__(self, partname, content_type, blob, image=None): + super(ImagePart, self).__init__(partname, content_type, blob) + self._image = image + + @property + def default_cx(self): + """ + Native width of this image, calculated from its width in pixels and + horizontal dots per inch (dpi). + """ + px_width = self.image.px_width + horz_dpi = self.image.horz_dpi + width_in_inches = px_width / horz_dpi + return Inches(width_in_inches) + + @property + def default_cy(self): + """ + Native height of this image, calculated from its height in pixels and + vertical dots per inch (dpi). + """ + px_height = self.image.px_height + horz_dpi = self.image.horz_dpi + height_in_emu = 914400 * px_height / horz_dpi + return Emu(height_in_emu) + + @property + def filename(self): + """ + Filename from which this image part was originally created. A generic + name, e.g. 'image.png', is substituted if no name is available, for + example when the image was loaded from an unnamed stream. In that + case a default extension is applied based on the detected MIME type + of the image. + """ + if self._image is not None: + return self._image.filename + return 'image.%s' % self.partname.ext + + @classmethod + def from_image(cls, image, partname): + """ + Return an |ImagePart| instance newly created from *image* and + assigned *partname*. + """ + return ImagePart(partname, image.content_type, image.blob, image) + + @property + def image(self): + if self._image is None: + self._image = Image.from_blob(self.blob) + return self._image + + @classmethod + def load(cls, partname, content_type, blob, package): + """ + Called by ``docx.opc.package.PartFactory`` to load an image part from + a package being opened by ``Document(...)`` call. + """ + return cls(partname, content_type, blob) + + @property + def sha1(self): + """ + SHA1 hash digest of the blob of this image part. + """ + return hashlib.sha1(self._blob).hexdigest() diff --git a/build/lib/docx/parts/numbering.py b/build/lib/docx/parts/numbering.py new file mode 100644 index 000000000..e324c5aac --- /dev/null +++ b/build/lib/docx/parts/numbering.py @@ -0,0 +1,47 @@ +# encoding: utf-8 + +""" +|NumberingPart| and closely related objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..opc.part import XmlPart +from ..shared import lazyproperty + + +class NumberingPart(XmlPart): + """ + Proxy for the numbering.xml part containing numbering definitions for + a document or glossary. + """ + @classmethod + def new(cls): + """ + Return newly created empty numbering part, containing only the root + ```` element. + """ + raise NotImplementedError + + @lazyproperty + def numbering_definitions(self): + """ + The |_NumberingDefinitions| instance containing the numbering + definitions ( element proxies) for this numbering part. + """ + return _NumberingDefinitions(self._element) + + +class _NumberingDefinitions(object): + """ + Collection of |_NumberingDefinition| instances corresponding to the + ```` elements in a numbering part. + """ + def __init__(self, numbering_elm): + super(_NumberingDefinitions, self).__init__() + self._numbering = numbering_elm + + def __len__(self): + return len(self._numbering.num_lst) diff --git a/build/lib/docx/parts/settings.py b/build/lib/docx/parts/settings.py new file mode 100644 index 000000000..a701b1726 --- /dev/null +++ b/build/lib/docx/parts/settings.py @@ -0,0 +1,54 @@ +# encoding: utf-8 + +""" +|SettingsPart| and closely related objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import os + +from ..opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI +from ..opc.part import XmlPart +from ..oxml import parse_xml +from ..settings import Settings + + +class SettingsPart(XmlPart): + """ + Document-level settings part of a WordprocessingML (WML) package. + """ + @classmethod + def default(cls, package): + """ + Return a newly created settings part, containing a default + `w:settings` element tree. + """ + partname = PackURI('/word/settings.xml') + content_type = CT.WML_SETTINGS + element = parse_xml(cls._default_settings_xml()) + return cls(partname, content_type, element, package) + + @property + def settings(self): + """ + A |Settings| proxy object for the `w:settings` element in this part, + containing the document-level settings for this document. + """ + return Settings(self.element) + + @classmethod + def _default_settings_xml(cls): + """ + Return a bytestream containing XML for a default settings part. + """ + path = os.path.join( + os.path.split(__file__)[0], '..', 'templates', + 'default-settings.xml' + ) + with open(path, 'rb') as f: + xml_bytes = f.read() + return xml_bytes diff --git a/build/lib/docx/parts/styles.py b/build/lib/docx/parts/styles.py new file mode 100644 index 000000000..00c7cb3c3 --- /dev/null +++ b/build/lib/docx/parts/styles.py @@ -0,0 +1,55 @@ +# encoding: utf-8 + +""" +Provides StylesPart and related objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import os + +from ..opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI +from ..opc.part import XmlPart +from ..oxml import parse_xml +from ..styles.styles import Styles + + +class StylesPart(XmlPart): + """ + Proxy for the styles.xml part containing style definitions for a document + or glossary. + """ + @classmethod + def default(cls, package): + """ + Return a newly created styles part, containing a default set of + elements. + """ + partname = PackURI('/word/styles.xml') + content_type = CT.WML_STYLES + element = parse_xml(cls._default_styles_xml()) + return cls(partname, content_type, element, package) + + @property + def styles(self): + """ + The |_Styles| instance containing the styles ( element + proxies) for this styles part. + """ + return Styles(self.element) + + @classmethod + def _default_styles_xml(cls): + """ + Return a bytestream containing XML for a default styles part. + """ + path = os.path.join( + os.path.split(__file__)[0], '..', 'templates', + 'default-styles.xml' + ) + with open(path, 'rb') as f: + xml_bytes = f.read() + return xml_bytes diff --git a/build/lib/docx/section.py b/build/lib/docx/section.py new file mode 100644 index 000000000..16221243b --- /dev/null +++ b/build/lib/docx/section.py @@ -0,0 +1,185 @@ +# encoding: utf-8 + +""" +The |Section| object and related proxy classes. +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from collections import Sequence + + +class Sections(Sequence): + """ + Sequence of |Section| objects corresponding to the sections in the + document. Supports ``len()``, iteration, and indexed access. + """ + def __init__(self, document_elm): + super(Sections, self).__init__() + self._document_elm = document_elm + + def __getitem__(self, key): + if isinstance(key, slice): + sectPr_lst = self._document_elm.sectPr_lst[key] + return [Section(sectPr) for sectPr in sectPr_lst] + sectPr = self._document_elm.sectPr_lst[key] + return Section(sectPr) + + def __iter__(self): + for sectPr in self._document_elm.sectPr_lst: + yield Section(sectPr) + + def __len__(self): + return len(self._document_elm.sectPr_lst) + + +class Section(object): + """ + Document section, providing access to section and page setup settings. + """ + def __init__(self, sectPr): + super(Section, self).__init__() + self._sectPr = sectPr + + @property + def bottom_margin(self): + """ + |Length| object representing the bottom margin for all pages in this + section in English Metric Units. + """ + return self._sectPr.bottom_margin + + @bottom_margin.setter + def bottom_margin(self, value): + self._sectPr.bottom_margin = value + + @property + def footer_distance(self): + """ + |Length| object representing the distance from the bottom edge of the + page to the bottom edge of the footer. |None| if no setting is present + in the XML. + """ + return self._sectPr.footer + + @footer_distance.setter + def footer_distance(self, value): + self._sectPr.footer = value + + @property + def gutter(self): + """ + |Length| object representing the page gutter size in English Metric + Units for all pages in this section. The page gutter is extra spacing + added to the *inner* margin to ensure even margins after page + binding. + """ + return self._sectPr.gutter + + @gutter.setter + def gutter(self, value): + self._sectPr.gutter = value + + @property + def header_distance(self): + """ + |Length| object representing the distance from the top edge of the + page to the top edge of the header. |None| if no setting is present + in the XML. + """ + return self._sectPr.header + + @header_distance.setter + def header_distance(self, value): + self._sectPr.header = value + + @property + def left_margin(self): + """ + |Length| object representing the left margin for all pages in this + section in English Metric Units. + """ + return self._sectPr.left_margin + + @left_margin.setter + def left_margin(self, value): + self._sectPr.left_margin = value + + @property + def orientation(self): + """ + Member of the :ref:`WdOrientation` enumeration specifying the page + orientation for this section, one of ``WD_ORIENT.PORTRAIT`` or + ``WD_ORIENT.LANDSCAPE``. + """ + return self._sectPr.orientation + + @orientation.setter + def orientation(self, value): + self._sectPr.orientation = value + + @property + def page_height(self): + """ + Total page height used for this section, inclusive of all edge spacing + values such as margins. Page orientation is taken into account, so + for example, its expected value would be ``Inches(8.5)`` for + letter-sized paper when orientation is landscape. + """ + return self._sectPr.page_height + + @page_height.setter + def page_height(self, value): + self._sectPr.page_height = value + + @property + def page_width(self): + """ + Total page width used for this section, inclusive of all edge spacing + values such as margins. Page orientation is taken into account, so + for example, its expected value would be ``Inches(11)`` for + letter-sized paper when orientation is landscape. + """ + return self._sectPr.page_width + + @page_width.setter + def page_width(self, value): + self._sectPr.page_width = value + + @property + def right_margin(self): + """ + |Length| object representing the right margin for all pages in this + section in English Metric Units. + """ + return self._sectPr.right_margin + + @right_margin.setter + def right_margin(self, value): + self._sectPr.right_margin = value + + @property + def start_type(self): + """ + The member of the :ref:`WdSectionStart` enumeration corresponding to + the initial break behavior of this section, e.g. + ``WD_SECTION.ODD_PAGE`` if the section should begin on the next odd + page. + """ + return self._sectPr.start_type + + @start_type.setter + def start_type(self, value): + self._sectPr.start_type = value + + @property + def top_margin(self): + """ + |Length| object representing the top margin for all pages in this + section in English Metric Units. + """ + return self._sectPr.top_margin + + @top_margin.setter + def top_margin(self, value): + self._sectPr.top_margin = value diff --git a/build/lib/docx/settings.py b/build/lib/docx/settings.py new file mode 100644 index 000000000..737146697 --- /dev/null +++ b/build/lib/docx/settings.py @@ -0,0 +1,20 @@ +# encoding: utf-8 + +""" +Settings object, providing access to document-level settings. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .shared import ElementProxy + + +class Settings(ElementProxy): + """ + Provides access to document-level settings for a document. Accessed using + the :attr:`.Document.settings` property. + """ + + __slots__ = () diff --git a/build/lib/docx/shape.py b/build/lib/docx/shape.py new file mode 100644 index 000000000..e4f885d73 --- /dev/null +++ b/build/lib/docx/shape.py @@ -0,0 +1,103 @@ +# encoding: utf-8 + +""" +Objects related to shapes, visual objects that appear on the drawing layer of +a document. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .enum.shape import WD_INLINE_SHAPE +from .oxml.ns import nsmap +from .shared import Parented + + +class InlineShapes(Parented): + """ + Sequence of |InlineShape| instances, supporting len(), iteration, and + indexed access. + """ + def __init__(self, body_elm, parent): + super(InlineShapes, self).__init__(parent) + self._body = body_elm + + def __getitem__(self, idx): + """ + Provide indexed access, e.g. 'inline_shapes[idx]' + """ + try: + inline = self._inline_lst[idx] + except IndexError: + msg = "inline shape index [%d] out of range" % idx + raise IndexError(msg) + return InlineShape(inline) + + def __iter__(self): + return (InlineShape(inline) for inline in self._inline_lst) + + def __len__(self): + return len(self._inline_lst) + + @property + def _inline_lst(self): + body = self._body + xpath = '//w:p/w:r/w:drawing/wp:inline' + return body.xpath(xpath) + + +class InlineShape(object): + """ + Proxy for an ```` element, representing the container for an + inline graphical object. + """ + def __init__(self, inline): + super(InlineShape, self).__init__() + self._inline = inline + + @property + def height(self): + """ + Read/write. The display height of this inline shape as an |Emu| + instance. + """ + return self._inline.extent.cy + + @height.setter + def height(self, cy): + self._inline.extent.cy = cy + self._inline.graphic.graphicData.pic.spPr.cy = cy + + @property + def type(self): + """ + The type of this inline shape as a member of + ``docx.enum.shape.WD_INLINE_SHAPE``, e.g. ``LINKED_PICTURE``. + Read-only. + """ + graphicData = self._inline.graphic.graphicData + uri = graphicData.uri + if uri == nsmap['pic']: + blip = graphicData.pic.blipFill.blip + if blip.link is not None: + return WD_INLINE_SHAPE.LINKED_PICTURE + return WD_INLINE_SHAPE.PICTURE + if uri == nsmap['c']: + return WD_INLINE_SHAPE.CHART + if uri == nsmap['dgm']: + return WD_INLINE_SHAPE.SMART_ART + return WD_INLINE_SHAPE.NOT_IMPLEMENTED + + @property + def width(self): + """ + Read/write. The display width of this inline shape as an |Emu| + instance. + """ + return self._inline.extent.cx + + @width.setter + def width(self, cx): + self._inline.extent.cx = cx + self._inline.graphic.graphicData.pic.spPr.cx = cx diff --git a/build/lib/docx/shared.py b/build/lib/docx/shared.py new file mode 100644 index 000000000..919964325 --- /dev/null +++ b/build/lib/docx/shared.py @@ -0,0 +1,250 @@ +# encoding: utf-8 + +""" +Objects shared by docx modules. +""" + +from __future__ import absolute_import, print_function, unicode_literals + + +class Length(int): + """ + Base class for length constructor classes Inches, Cm, Mm, Px, and Emu. + Behaves as an int count of English Metric Units, 914,400 to the inch, + 36,000 to the mm. Provides convenience unit conversion methods in the form + of read-only properties. Immutable. + """ + _EMUS_PER_INCH = 914400 + _EMUS_PER_CM = 360000 + _EMUS_PER_MM = 36000 + _EMUS_PER_PT = 12700 + _EMUS_PER_TWIP = 635 + + def __new__(cls, emu): + return int.__new__(cls, emu) + + @property + def cm(self): + """ + The equivalent length expressed in centimeters (float). + """ + return self / float(self._EMUS_PER_CM) + + @property + def emu(self): + """ + The equivalent length expressed in English Metric Units (int). + """ + return self + + @property + def inches(self): + """ + The equivalent length expressed in inches (float). + """ + return self / float(self._EMUS_PER_INCH) + + @property + def mm(self): + """ + The equivalent length expressed in millimeters (float). + """ + return self / float(self._EMUS_PER_MM) + + @property + def pt(self): + """ + Floating point length in points + """ + return self / float(self._EMUS_PER_PT) + + @property + def twips(self): + """ + The equivalent length expressed in twips (int). + """ + return int(round(self / float(self._EMUS_PER_TWIP))) + + +class Inches(Length): + """ + Convenience constructor for length in inches, e.g. + ``width = Inches(0.5)``. + """ + def __new__(cls, inches): + emu = int(inches * Length._EMUS_PER_INCH) + return Length.__new__(cls, emu) + + +class Cm(Length): + """ + Convenience constructor for length in centimeters, e.g. + ``height = Cm(12)``. + """ + def __new__(cls, cm): + emu = int(cm * Length._EMUS_PER_CM) + return Length.__new__(cls, emu) + + +class Emu(Length): + """ + Convenience constructor for length in English Metric Units, e.g. + ``width = Emu(457200)``. + """ + def __new__(cls, emu): + return Length.__new__(cls, int(emu)) + + +class Mm(Length): + """ + Convenience constructor for length in millimeters, e.g. + ``width = Mm(240.5)``. + """ + def __new__(cls, mm): + emu = int(mm * Length._EMUS_PER_MM) + return Length.__new__(cls, emu) + + +class Pt(Length): + """ + Convenience value class for specifying a length in points + """ + def __new__(cls, points): + emu = int(points * Length._EMUS_PER_PT) + return Length.__new__(cls, emu) + + +class Twips(Length): + """ + Convenience constructor for length in twips, e.g. ``width = Twips(42)``. + A twip is a twentieth of a point, 635 EMU. + """ + def __new__(cls, twips): + emu = int(twips * Length._EMUS_PER_TWIP) + return Length.__new__(cls, emu) + + +class RGBColor(tuple): + """ + Immutable value object defining a particular RGB color. + """ + def __new__(cls, r, g, b): + msg = 'RGBColor() takes three integer values 0-255' + for val in (r, g, b): + if not isinstance(val, int) or val < 0 or val > 255: + raise ValueError(msg) + return super(RGBColor, cls).__new__(cls, (r, g, b)) + + def __repr__(self): + return 'RGBColor(0x%02x, 0x%02x, 0x%02x)' % self + + def __str__(self): + """ + Return a hex string rgb value, like '3C2F80' + """ + return '%02X%02X%02X' % self + + @classmethod + def from_string(cls, rgb_hex_str): + """ + Return a new instance from an RGB color hex string like ``'3C2F80'``. + """ + r = int(rgb_hex_str[:2], 16) + g = int(rgb_hex_str[2:4], 16) + b = int(rgb_hex_str[4:], 16) + return cls(r, g, b) + + +def lazyproperty(f): + """ + @lazyprop decorator. Decorated method will be called only on first access + to calculate a cached property value. After that, the cached value is + returned. + """ + cache_attr_name = '_%s' % f.__name__ # like '_foobar' for prop 'foobar' + docstring = f.__doc__ + + def get_prop_value(obj): + try: + return getattr(obj, cache_attr_name) + except AttributeError: + value = f(obj) + setattr(obj, cache_attr_name, value) + return value + + return property(get_prop_value, doc=docstring) + + +def write_only_property(f): + """ + @write_only_property decorator. Creates a property (descriptor attribute) + that accepts assignment, but not getattr (use in an expression). + """ + docstring = f.__doc__ + + return property(fset=f, doc=docstring) + + +class ElementProxy(object): + """ + Base class for lxml element proxy classes. An element proxy class is one + whose primary responsibilities are fulfilled by manipulating the + attributes and child elements of an XML element. They are the most common + type of class in python-docx other than custom element (oxml) classes. + """ + + __slots__ = ('_element', '_parent') + + def __init__(self, element, parent=None): + self._element = element + self._parent = parent + + def __eq__(self, other): + """ + Return |True| if this proxy object refers to the same oxml element as + does *other*. ElementProxy objects are value objects and should + maintain no mutable local state. Equality for proxy objects is + defined as referring to the same XML element, whether or not they are + the same proxy object instance. + """ + if not isinstance(other, ElementProxy): + return False + return self._element is other._element + + def __ne__(self, other): + if not isinstance(other, ElementProxy): + return True + return self._element is not other._element + + @property + def element(self): + """ + The lxml element proxied by this object. + """ + return self._element + + @property + def part(self): + """ + The package part containing this object + """ + return self._parent.part + + +class Parented(object): + """ + Provides common services for document elements that occur below a part + but may occasionally require an ancestor object to provide a service, + such as add or drop a relationship. Provides ``self._parent`` attribute + to subclasses. + """ + def __init__(self, parent): + super(Parented, self).__init__() + self._parent = parent + + @property + def part(self): + """ + The package part containing this object + """ + return self._parent.part diff --git a/build/lib/docx/styles/__init__.py b/build/lib/docx/styles/__init__.py new file mode 100644 index 000000000..3eff43e55 --- /dev/null +++ b/build/lib/docx/styles/__init__.py @@ -0,0 +1,50 @@ +# encoding: utf-8 + +""" +Sub-package module for docx.styles sub-package. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + + +class BabelFish(object): + """ + Translates special-case style names from UI name (e.g. Heading 1) to + internal/styles.xml name (e.g. heading 1) and back. + """ + + style_aliases = ( + ('Caption', 'caption'), + ('Heading 1', 'heading 1'), + ('Heading 2', 'heading 2'), + ('Heading 3', 'heading 3'), + ('Heading 4', 'heading 4'), + ('Heading 5', 'heading 5'), + ('Heading 6', 'heading 6'), + ('Heading 7', 'heading 7'), + ('Heading 8', 'heading 8'), + ('Heading 9', 'heading 9'), + ) + + internal_style_names = dict(style_aliases) + ui_style_names = dict((item[1], item[0]) for item in style_aliases) + + @classmethod + def ui2internal(cls, ui_style_name): + """ + Return the internal style name corresponding to *ui_style_name*, such + as 'heading 1' for 'Heading 1'. + """ + return cls.internal_style_names.get(ui_style_name, ui_style_name) + + @classmethod + def internal2ui(cls, internal_style_name): + """ + Return the user interface style name corresponding to + *internal_style_name*, such as 'Heading 1' for 'heading 1'. + """ + return cls.ui_style_names.get( + internal_style_name, internal_style_name + ) diff --git a/build/lib/docx/styles/latent.py b/build/lib/docx/styles/latent.py new file mode 100644 index 000000000..99b1514ff --- /dev/null +++ b/build/lib/docx/styles/latent.py @@ -0,0 +1,224 @@ +# encoding: utf-8 + +""" +Latent style-related objects. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from . import BabelFish +from ..shared import ElementProxy + + +class LatentStyles(ElementProxy): + """ + Provides access to the default behaviors for latent styles in this + document and to the collection of |_LatentStyle| objects that define + overrides of those defaults for a particular named latent style. + """ + + __slots__ = () + + def __getitem__(self, key): + """ + Enables dictionary-style access to a latent style by name. + """ + style_name = BabelFish.ui2internal(key) + lsdException = self._element.get_by_name(style_name) + if lsdException is None: + raise KeyError("no latent style with name '%s'" % key) + return _LatentStyle(lsdException) + + def __iter__(self): + return (_LatentStyle(ls) for ls in self._element.lsdException_lst) + + def __len__(self): + return len(self._element.lsdException_lst) + + def add_latent_style(self, name): + """ + Return a newly added |_LatentStyle| object to override the inherited + defaults defined in this latent styles object for the built-in style + having *name*. + """ + lsdException = self._element.add_lsdException() + lsdException.name = BabelFish.ui2internal(name) + return _LatentStyle(lsdException) + + @property + def default_priority(self): + """ + Integer between 0 and 99 inclusive specifying the default sort order + for latent styles in style lists and the style gallery. |None| if no + value is assigned, which causes Word to use the default value 99. + """ + return self._element.defUIPriority + + @default_priority.setter + def default_priority(self, value): + self._element.defUIPriority = value + + @property + def default_to_hidden(self): + """ + Boolean specifying whether the default behavior for latent styles is + to be hidden. A hidden style does not appear in the recommended list + or in the style gallery. + """ + return self._element.bool_prop('defSemiHidden') + + @default_to_hidden.setter + def default_to_hidden(self, value): + self._element.set_bool_prop('defSemiHidden', value) + + @property + def default_to_locked(self): + """ + Boolean specifying whether the default behavior for latent styles is + to be locked. A locked style does not appear in the styles panel or + the style gallery and cannot be applied to document content. This + behavior is only active when formatting protection is turned on for + the document (via the Developer menu). + """ + return self._element.bool_prop('defLockedState') + + @default_to_locked.setter + def default_to_locked(self, value): + self._element.set_bool_prop('defLockedState', value) + + @property + def default_to_quick_style(self): + """ + Boolean specifying whether the default behavior for latent styles is + to appear in the style gallery when not hidden. + """ + return self._element.bool_prop('defQFormat') + + @default_to_quick_style.setter + def default_to_quick_style(self, value): + self._element.set_bool_prop('defQFormat', value) + + @property + def default_to_unhide_when_used(self): + """ + Boolean specifying whether the default behavior for latent styles is + to be unhidden when first applied to content. + """ + return self._element.bool_prop('defUnhideWhenUsed') + + @default_to_unhide_when_used.setter + def default_to_unhide_when_used(self, value): + self._element.set_bool_prop('defUnhideWhenUsed', value) + + @property + def load_count(self): + """ + Integer specifying the number of built-in styles to initialize to the + defaults specified in this |LatentStyles| object. |None| if there is + no setting in the XML (very uncommon). The default Word 2011 template + sets this value to 276, accounting for the built-in styles in Word + 2010. + """ + return self._element.count + + @load_count.setter + def load_count(self, value): + self._element.count = value + + +class _LatentStyle(ElementProxy): + """ + Proxy for an `w:lsdException` element, which specifies display behaviors + for a built-in style when no definition for that style is stored yet in + the `styles.xml` part. The values in this element override the defaults + specified in the parent `w:latentStyles` element. + """ + + __slots__ = () + + def delete(self): + """ + Remove this latent style definition such that the defaults defined in + the containing |LatentStyles| object provide the effective value for + each of its attributes. Attempting to access any attributes on this + object after calling this method will raise |AttributeError|. + """ + self._element.delete() + self._element = None + + @property + def hidden(self): + """ + Tri-state value specifying whether this latent style should appear in + the recommended list. |None| indicates the effective value is + inherited from the parent ```` element. + """ + return self._element.on_off_prop('semiHidden') + + @hidden.setter + def hidden(self, value): + self._element.set_on_off_prop('semiHidden', value) + + @property + def locked(self): + """ + Tri-state value specifying whether this latent styles is locked. + A locked style does not appear in the styles panel or the style + gallery and cannot be applied to document content. This behavior is + only active when formatting protection is turned on for the document + (via the Developer menu). + """ + return self._element.on_off_prop('locked') + + @locked.setter + def locked(self, value): + self._element.set_on_off_prop('locked', value) + + @property + def name(self): + """ + The name of the built-in style this exception applies to. + """ + return BabelFish.internal2ui(self._element.name) + + @property + def priority(self): + """ + The integer sort key for this latent style in the Word UI. + """ + return self._element.uiPriority + + @priority.setter + def priority(self, value): + self._element.uiPriority = value + + @property + def quick_style(self): + """ + Tri-state value specifying whether this latent style should appear in + the Word styles gallery when not hidden. |None| indicates the + effective value should be inherited from the default values in its + parent |LatentStyles| object. + """ + return self._element.on_off_prop('qFormat') + + @quick_style.setter + def quick_style(self, value): + self._element.set_on_off_prop('qFormat', value) + + @property + def unhide_when_used(self): + """ + Tri-state value specifying whether this style should have its + :attr:`hidden` attribute set |False| the next time the style is + applied to content. |None| indicates the effective value should be + inherited from the default specified by its parent |LatentStyles| + object. + """ + return self._element.on_off_prop('unhideWhenUsed') + + @unhide_when_used.setter + def unhide_when_used(self, value): + self._element.set_on_off_prop('unhideWhenUsed', value) diff --git a/build/lib/docx/styles/style.py b/build/lib/docx/styles/style.py new file mode 100644 index 000000000..24371b231 --- /dev/null +++ b/build/lib/docx/styles/style.py @@ -0,0 +1,265 @@ +# encoding: utf-8 + +""" +Style object hierarchy. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from . import BabelFish +from ..enum.style import WD_STYLE_TYPE +from ..shared import ElementProxy +from ..text.font import Font +from ..text.parfmt import ParagraphFormat + + +def StyleFactory(style_elm): + """ + Return a style object of the appropriate |BaseStyle| subclass, according + to the type of *style_elm*. + """ + style_cls = { + WD_STYLE_TYPE.PARAGRAPH: _ParagraphStyle, + WD_STYLE_TYPE.CHARACTER: _CharacterStyle, + WD_STYLE_TYPE.TABLE: _TableStyle, + WD_STYLE_TYPE.LIST: _NumberingStyle + }[style_elm.type] + + return style_cls(style_elm) + + +class BaseStyle(ElementProxy): + """ + Base class for the various types of style object, paragraph, character, + table, and numbering. These properties and methods are inherited by all + style objects. + """ + + __slots__ = () + + @property + def builtin(self): + """ + Read-only. |True| if this style is a built-in style. |False| + indicates it is a custom (user-defined) style. Note this value is + based on the presence of a `customStyle` attribute in the XML, not on + specific knowledge of which styles are built into Word. + """ + return not self._element.customStyle + + def delete(self): + """ + Remove this style definition from the document. Note that calling + this method does not remove or change the style applied to any + document content. Content items having the deleted style will be + rendered using the default style, as is any content with a style not + defined in the document. + """ + self._element.delete() + self._element = None + + @property + def hidden(self): + """ + |True| if display of this style in the style gallery and list of + recommended styles is suppressed. |False| otherwise. In order to be + shown in the style gallery, this value must be |False| and + :attr:`.quick_style` must be |True|. + """ + return self._element.semiHidden_val + + @hidden.setter + def hidden(self, value): + self._element.semiHidden_val = value + + @property + def locked(self): + """ + Read/write Boolean. |True| if this style is locked. A locked style + does not appear in the styles panel or the style gallery and cannot + be applied to document content. This behavior is only active when + formatting protection is turned on for the document (via the + Developer menu). + """ + return self._element.locked_val + + @locked.setter + def locked(self, value): + self._element.locked_val = value + + @property + def name(self): + """ + The UI name of this style. + """ + name = self._element.name_val + if name is None: + return None + return BabelFish.internal2ui(name) + + @name.setter + def name(self, value): + self._element.name_val = value + + @property + def priority(self): + """ + The integer sort key governing display sequence of this style in the + Word UI. |None| indicates no setting is defined, causing Word to use + the default value of 0. Style name is used as a secondary sort key to + resolve ordering of styles having the same priority value. + """ + return self._element.uiPriority_val + + @priority.setter + def priority(self, value): + self._element.uiPriority_val = value + + @property + def quick_style(self): + """ + |True| if this style should be displayed in the style gallery when + :attr:`.hidden` is |False|. Read/write Boolean. + """ + return self._element.qFormat_val + + @quick_style.setter + def quick_style(self, value): + self._element.qFormat_val = value + + @property + def style_id(self): + """ + The unique key name (string) for this style. This value is subject to + rewriting by Word and should generally not be changed unless you are + familiar with the internals involved. + """ + return self._element.styleId + + @style_id.setter + def style_id(self, value): + self._element.styleId = value + + @property + def type(self): + """ + Member of :ref:`WdStyleType` corresponding to the type of this style, + e.g. ``WD_STYLE_TYPE.PARAGRAPH``. + """ + type = self._element.type + if type is None: + return WD_STYLE_TYPE.PARAGRAPH + return type + + @property + def unhide_when_used(self): + """ + |True| if an application should make this style visible the next time + it is applied to content. False otherwise. Note that |docx| does not + automatically unhide a style having |True| for this attribute when it + is applied to content. + """ + return self._element.unhideWhenUsed_val + + @unhide_when_used.setter + def unhide_when_used(self, value): + self._element.unhideWhenUsed_val = value + + +class _CharacterStyle(BaseStyle): + """ + A character style. A character style is applied to a |Run| object and + primarily provides character-level formatting via the |Font| object in + its :attr:`.font` property. + """ + + __slots__ = () + + @property + def base_style(self): + """ + Style object this style inherits from or |None| if this style is + not based on another style. + """ + base_style = self._element.base_style + if base_style is None: + return None + return StyleFactory(base_style) + + @base_style.setter + def base_style(self, style): + style_id = style.style_id if style is not None else None + self._element.basedOn_val = style_id + + @property + def font(self): + """ + The |Font| object providing access to the character formatting + properties for this style, such as font name and size. + """ + return Font(self._element) + + +class _ParagraphStyle(_CharacterStyle): + """ + A paragraph style. A paragraph style provides both character formatting + and paragraph formatting such as indentation and line-spacing. + """ + + __slots__ = () + + def __repr__(self): + return '_ParagraphStyle(\'%s\') id: %s' % (self.name, id(self)) + + @property + def next_paragraph_style(self): + """ + |_ParagraphStyle| object representing the style to be applied + automatically to a new paragraph inserted after a paragraph of this + style. Returns self if no next paragraph style is defined. Assigning + |None| or *self* removes the setting such that new paragraphs are + created using this same style. + """ + next_style_elm = self._element.next_style + if next_style_elm is None: + return self + if next_style_elm.type != WD_STYLE_TYPE.PARAGRAPH: + return self + return StyleFactory(next_style_elm) + + @next_paragraph_style.setter + def next_paragraph_style(self, style): + if style is None or style.style_id == self.style_id: + self._element._remove_next() + else: + self._element.get_or_add_next().val = style.style_id + + @property + def paragraph_format(self): + """ + The |ParagraphFormat| object providing access to the paragraph + formatting properties for this style such as indentation. + """ + return ParagraphFormat(self._element) + + +class _TableStyle(_ParagraphStyle): + """ + A table style. A table style provides character and paragraph formatting + for its contents as well as special table formatting properties. + """ + + __slots__ = () + + def __repr__(self): + return '_TableStyle(\'%s\') id: %s' % (self.name, id(self)) + + +class _NumberingStyle(BaseStyle): + """ + A numbering style. Not yet implemented. + """ + + __slots__ = () diff --git a/build/lib/docx/styles/styles.py b/build/lib/docx/styles/styles.py new file mode 100644 index 000000000..ad27fa764 --- /dev/null +++ b/build/lib/docx/styles/styles.py @@ -0,0 +1,157 @@ +# encoding: utf-8 + +""" +Styles object, container for all objects in the styles part. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from warnings import warn + +from . import BabelFish +from .latent import LatentStyles +from ..shared import ElementProxy +from .style import BaseStyle, StyleFactory + + +class Styles(ElementProxy): + """ + A collection providing access to the styles defined in a document. + Accessed using the :attr:`.Document.styles` property. Supports ``len()``, + iteration, and dictionary-style access by style name. + """ + + __slots__ = () + + def __contains__(self, name): + """ + Enables `in` operator on style name. + """ + internal_name = BabelFish.ui2internal(name) + for style in self._element.style_lst: + if style.name_val == internal_name: + return True + return False + + def __getitem__(self, key): + """ + Enables dictionary-style access by UI name. Lookup by style id is + deprecated, triggers a warning, and will be removed in a near-future + release. + """ + style_elm = self._element.get_by_name(BabelFish.ui2internal(key)) + if style_elm is not None: + return StyleFactory(style_elm) + + style_elm = self._element.get_by_id(key) + if style_elm is not None: + msg = ( + 'style lookup by style_id is deprecated. Use style name as ' + 'key instead.' + ) + warn(msg, UserWarning, stacklevel=2) + return StyleFactory(style_elm) + + raise KeyError("no style with name '%s'" % key) + + def __iter__(self): + return (StyleFactory(style) for style in self._element.style_lst) + + def __len__(self): + return len(self._element.style_lst) + + def add_style(self, name, style_type, builtin=False): + """ + Return a newly added style object of *style_type* and identified + by *name*. A builtin style can be defined by passing True for the + optional *builtin* argument. + """ + style_name = BabelFish.ui2internal(name) + if style_name in self: + raise ValueError("document already contains style '%s'" % name) + style = self._element.add_style_of_type( + style_name, style_type, builtin + ) + return StyleFactory(style) + + def default(self, style_type): + """ + Return the default style for *style_type* or |None| if no default is + defined for that type (not common). + """ + style = self._element.default_for(style_type) + if style is None: + return None + return StyleFactory(style) + + def get_by_id(self, style_id, style_type): + """ + Return the style of *style_type* matching *style_id*. Returns the + default for *style_type* if *style_id* is not found or is |None|, or + if the style having *style_id* is not of *style_type*. + """ + if style_id is None: + return self.default(style_type) + return self._get_by_id(style_id, style_type) + + def get_style_id(self, style_or_name, style_type): + """ + Return the id of the style corresponding to *style_or_name*, or + |None| if *style_or_name* is |None|. If *style_or_name* is not + a style object, the style is looked up using *style_or_name* as + a style name, raising |ValueError| if no style with that name is + defined. Raises |ValueError| if the target style is not of + *style_type*. + """ + if style_or_name is None: + return None + elif isinstance(style_or_name, BaseStyle): + return self._get_style_id_from_style(style_or_name, style_type) + else: + return self._get_style_id_from_name(style_or_name, style_type) + + @property + def latent_styles(self): + """ + A |LatentStyles| object providing access to the default behaviors for + latent styles and the collection of |_LatentStyle| objects that + define overrides of those defaults for a particular named latent + style. + """ + return LatentStyles(self._element.get_or_add_latentStyles()) + + def _get_by_id(self, style_id, style_type): + """ + Return the style of *style_type* matching *style_id*. Returns the + default for *style_type* if *style_id* is not found or if the style + having *style_id* is not of *style_type*. + """ + style = self._element.get_by_id(style_id) + if style is None or style.type != style_type: + return self.default(style_type) + return StyleFactory(style) + + def _get_style_id_from_name(self, style_name, style_type): + """ + Return the id of the style of *style_type* corresponding to + *style_name*. Returns |None| if that style is the default style for + *style_type*. Raises |ValueError| if the named style is not found in + the document or does not match *style_type*. + """ + return self._get_style_id_from_style(self[style_name], style_type) + + def _get_style_id_from_style(self, style, style_type): + """ + Return the id of *style*, or |None| if it is the default style of + *style_type*. Raises |ValueError| if style is not of *style_type*. + """ + if style.type != style_type: + raise ValueError( + "assigned style is type %s, need type %s" % + (style.type, style_type) + ) + if style == self.default(style_type): + return None + return style.style_id diff --git a/build/lib/docx/table.py b/build/lib/docx/table.py new file mode 100644 index 000000000..b3bc090fb --- /dev/null +++ b/build/lib/docx/table.py @@ -0,0 +1,469 @@ +# encoding: utf-8 + +""" +The |Table| object and related proxy classes. +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from .blkcntnr import BlockItemContainer +from .enum.style import WD_STYLE_TYPE +from .oxml.simpletypes import ST_Merge +from .shared import Inches, lazyproperty, Parented + + +class Table(Parented): + """ + Proxy class for a WordprocessingML ```` element. + """ + def __init__(self, tbl, parent): + super(Table, self).__init__(parent) + self._element = self._tbl = tbl + + def add_column(self, width): + """ + Return a |_Column| object of *width*, newly added rightmost to the + table. + """ + tblGrid = self._tbl.tblGrid + gridCol = tblGrid.add_gridCol() + gridCol.w = width + for tr in self._tbl.tr_lst: + tc = tr.add_tc() + tc.width = width + return _Column(gridCol, self) + + def add_row(self): + """ + Return a |_Row| instance, newly added bottom-most to the table. + """ + tbl = self._tbl + tr = tbl.add_tr() + for gridCol in tbl.tblGrid.gridCol_lst: + tc = tr.add_tc() + tc.width = gridCol.w + return _Row(tr, self) + + @property + def alignment(self): + """ + Read/write. A member of :ref:`WdRowAlignment` or None, specifying the + positioning of this table between the page margins. |None| if no + setting is specified, causing the effective value to be inherited + from the style hierarchy. + """ + return self._tblPr.alignment + + @alignment.setter + def alignment(self, value): + self._tblPr.alignment = value + + @property + def autofit(self): + """ + |True| if column widths can be automatically adjusted to improve the + fit of cell contents. |False| if table layout is fixed. Column widths + are adjusted in either case if total column width exceeds page width. + Read/write boolean. + """ + return self._tblPr.autofit + + @autofit.setter + def autofit(self, value): + self._tblPr.autofit = value + + def cell(self, row_idx, col_idx): + """ + Return |_Cell| instance correponding to table cell at *row_idx*, + *col_idx* intersection, where (0, 0) is the top, left-most cell. + """ + cell_idx = col_idx + (row_idx * self._column_count) + return self._cells[cell_idx] + + def column_cells(self, column_idx): + """ + Sequence of cells in the column at *column_idx* in this table. + """ + cells = self._cells + idxs = range(column_idx, len(cells), self._column_count) + return [cells[idx] for idx in idxs] + + @lazyproperty + def columns(self): + """ + |_Columns| instance representing the sequence of columns in this + table. + """ + return _Columns(self._tbl, self) + + def row_cells(self, row_idx): + """ + Sequence of cells in the row at *row_idx* in this table. + """ + column_count = self._column_count + start = row_idx * column_count + end = start + column_count + return self._cells[start:end] + + @lazyproperty + def rows(self): + """ + |_Rows| instance containing the sequence of rows in this table. + """ + return _Rows(self._tbl, self) + + @property + def style(self): + """ + Read/write. A |_TableStyle| object representing the style applied to + this table. The default table style for the document (often `Normal + Table`) is returned if the table has no directly-applied style. + Assigning |None| to this property removes any directly-applied table + style causing it to inherit the default table style of the document. + Note that the style name of a table style differs slightly from that + displayed in the user interface; a hyphen, if it appears, must be + removed. For example, `Light Shading - Accent 1` becomes `Light + Shading Accent 1`. + """ + style_id = self._tbl.tblStyle_val + return self.part.get_style(style_id, WD_STYLE_TYPE.TABLE) + + @style.setter + def style(self, style_or_name): + style_id = self.part.get_style_id( + style_or_name, WD_STYLE_TYPE.TABLE + ) + self._tbl.tblStyle_val = style_id + + @property + def table(self): + """ + Provide child objects with reference to the |Table| object they + belong to, without them having to know their direct parent is + a |Table| object. This is the terminus of a series of `parent._table` + calls from an arbitrary child through its ancestors. + """ + return self + + @property + def table_direction(self): + """ + A member of :ref:`WdTableDirection` indicating the direction in which + the table cells are ordered, e.g. `WD_TABLE_DIRECTION.LTR`. |None| + indicates the value is inherited from the style hierarchy. + """ + return self._element.bidiVisual_val + + @table_direction.setter + def table_direction(self, value): + self._element.bidiVisual_val = value + + @property + def _cells(self): + """ + A sequence of |_Cell| objects, one for each cell of the layout grid. + If the table contains a span, one or more |_Cell| object references + are repeated. + """ + col_count = self._column_count + cells = [] + for tc in self._tbl.iter_tcs(): + for grid_span_idx in range(tc.grid_span): + if tc.vMerge == ST_Merge.CONTINUE: + cells.append(cells[-col_count]) + elif grid_span_idx > 0: + cells.append(cells[-1]) + else: + cells.append(_Cell(tc, self)) + return cells + + @property + def _column_count(self): + """ + The number of grid columns in this table. + """ + return self._tbl.col_count + + @property + def _tblPr(self): + return self._tbl.tblPr + + +class _Cell(BlockItemContainer): + """Table cell""" + + def __init__(self, tc, parent): + super(_Cell, self).__init__(tc, parent) + self._tc = self._element = tc + + def add_paragraph(self, text='', style=None): + """ + Return a paragraph newly added to the end of the content in this + cell. If present, *text* is added to the paragraph in a single run. + If specified, the paragraph style *style* is applied. If *style* is + not specified or is |None|, the result is as though the 'Normal' + style was applied. Note that the formatting of text in a cell can be + influenced by the table style. *text* can contain tab (``\\t``) + characters, which are converted to the appropriate XML form for + a tab. *text* can also include newline (``\\n``) or carriage return + (``\\r``) characters, each of which is converted to a line break. + """ + return super(_Cell, self).add_paragraph(text, style) + + def add_table(self, rows, cols): + """ + Return a table newly added to this cell after any existing cell + content, having *rows* rows and *cols* columns. An empty paragraph is + added after the table because Word requires a paragraph element as + the last element in every cell. + """ + width = self.width if self.width is not None else Inches(1) + table = super(_Cell, self).add_table(rows, cols, width) + self.add_paragraph() + return table + + def merge(self, other_cell): + """ + Return a merged cell created by spanning the rectangular region + having this cell and *other_cell* as diagonal corners. Raises + |InvalidSpanError| if the cells do not define a rectangular region. + """ + tc, tc_2 = self._tc, other_cell._tc + merged_tc = tc.merge(tc_2) + return _Cell(merged_tc, self._parent) + + @property + def paragraphs(self): + """ + List of paragraphs in the cell. A table cell is required to contain + at least one block-level element and end with a paragraph. By + default, a new cell contains a single paragraph. Read-only + """ + return super(_Cell, self).paragraphs + + @property + def tables(self): + """ + List of tables in the cell, in the order they appear. Read-only. + """ + return super(_Cell, self).tables + + @property + def text(self): + """ + The entire contents of this cell as a string of text. Assigning + a string to this property replaces all existing content with a single + paragraph containing the assigned text in a single run. + """ + return '\n'.join(p.text for p in self.paragraphs) + + @text.setter + def text(self, text): + """ + Write-only. Set entire contents of cell to the string *text*. Any + existing content or revisions are replaced. + """ + tc = self._tc + tc.clear_content() + p = tc.add_p() + r = p.add_r() + r.text = text + + @property + def vertical_alignment(self): + """Member of :ref:`WdCellVerticalAlignment` or None. + + A value of |None| indicates vertical alignment for this cell is + inherited. Assigning |None| causes any explicitly defined vertical + alignment to be removed, restoring inheritance. + """ + tcPr = self._element.tcPr + if tcPr is None: + return None + return tcPr.vAlign_val + + @vertical_alignment.setter + def vertical_alignment(self, value): + tcPr = self._element.get_or_add_tcPr() + tcPr.vAlign_val = value + + @property + def width(self): + """ + The width of this cell in EMU, or |None| if no explicit width is set. + """ + return self._tc.width + + @width.setter + def width(self, value): + self._tc.width = value + + +class _Column(Parented): + """ + Table column + """ + def __init__(self, gridCol, parent): + super(_Column, self).__init__(parent) + self._gridCol = gridCol + + @property + def cells(self): + """ + Sequence of |_Cell| instances corresponding to cells in this column. + """ + return tuple(self.table.column_cells(self._index)) + + @property + def table(self): + """ + Reference to the |Table| object this column belongs to. + """ + return self._parent.table + + @property + def width(self): + """ + The width of this column in EMU, or |None| if no explicit width is + set. + """ + return self._gridCol.w + + @width.setter + def width(self, value): + self._gridCol.w = value + + @property + def _index(self): + """ + Index of this column in its table, starting from zero. + """ + return self._gridCol.gridCol_idx + + +class _Columns(Parented): + """ + Sequence of |_Column| instances corresponding to the columns in a table. + Supports ``len()``, iteration and indexed access. + """ + def __init__(self, tbl, parent): + super(_Columns, self).__init__(parent) + self._tbl = tbl + + def __getitem__(self, idx): + """ + Provide indexed access, e.g. 'columns[0]' + """ + try: + gridCol = self._gridCol_lst[idx] + except IndexError: + msg = "column index [%d] is out of range" % idx + raise IndexError(msg) + return _Column(gridCol, self) + + def __iter__(self): + for gridCol in self._gridCol_lst: + yield _Column(gridCol, self) + + def __len__(self): + return len(self._gridCol_lst) + + @property + def table(self): + """ + Reference to the |Table| object this column collection belongs to. + """ + return self._parent.table + + @property + def _gridCol_lst(self): + """ + Sequence containing ```` elements for this table, each + representing a table column. + """ + tblGrid = self._tbl.tblGrid + return tblGrid.gridCol_lst + + +class _Row(Parented): + """ + Table row + """ + def __init__(self, tr, parent): + super(_Row, self).__init__(parent) + self._tr = self._element = tr + + @property + def cells(self): + """ + Sequence of |_Cell| instances corresponding to cells in this row. + """ + return tuple(self.table.row_cells(self._index)) + + @property + def height(self): + """ + Return a |Length| object representing the height of this cell, or + |None| if no explicit height is set. + """ + return self._tr.trHeight_val + + @height.setter + def height(self, value): + self._tr.trHeight_val = value + + @property + def height_rule(self): + """ + Return the height rule of this cell as a member of the + :ref:`WdRowHeightRule` enumeration, or |None| if no explicit + height_rule is set. + """ + return self._tr.trHeight_hRule + + @height_rule.setter + def height_rule(self, value): + self._tr.trHeight_hRule = value + + @property + def table(self): + """ + Reference to the |Table| object this row belongs to. + """ + return self._parent.table + + @property + def _index(self): + """ + Index of this row in its table, starting from zero. + """ + return self._tr.tr_idx + + +class _Rows(Parented): + """ + Sequence of |_Row| objects corresponding to the rows in a table. + Supports ``len()``, iteration, indexed access, and slicing. + """ + def __init__(self, tbl, parent): + super(_Rows, self).__init__(parent) + self._tbl = tbl + + def __getitem__(self, idx): + """ + Provide indexed access, (e.g. 'rows[0]') + """ + return list(self)[idx] + + def __iter__(self): + return (_Row(tr, self) for tr in self._tbl.tr_lst) + + def __len__(self): + return len(self._tbl.tr_lst) + + @property + def table(self): + """ + Reference to the |Table| object this row collection belongs to. + """ + return self._parent.table diff --git a/build/lib/docx/templates/default-comments.xml b/build/lib/docx/templates/default-comments.xml new file mode 100644 index 000000000..4ceb12ea4 --- /dev/null +++ b/build/lib/docx/templates/default-comments.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/build/lib/docx/templates/default-settings.xml b/build/lib/docx/templates/default-settings.xml new file mode 100644 index 000000000..fda1adef7 --- /dev/null +++ b/build/lib/docx/templates/default-settings.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + diff --git a/build/lib/docx/templates/default-src.docx b/build/lib/docx/templates/default-src.docx new file mode 100644 index 0000000000000000000000000000000000000000..31c8e20b4a21a0c45d9b22b1c3f68808fca37279 GIT binary patch literal 77650 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/lib/docx/templates/default.docx b/build/lib/docx/templates/default.docx new file mode 100644 index 0000000000000000000000000000000000000000..dd00cd2fc1bbea08f8a92e1ca0b1afd1e276d866 GIT binary patch literal 38003 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 element and providing + access to character properties such as font name, font size, bold, and + subscript. + """ + + __slots__ = () + + @property + def all_caps(self): + """ + Read/write. Causes text in this font to appear in capital letters. + """ + return self._get_bool_prop('caps') + + @all_caps.setter + def all_caps(self, value): + self._set_bool_prop('caps', value) + + @property + def bold(self): + """ + Read/write. Causes text in this font to appear in bold. + """ + return self._get_bool_prop('b') + + @bold.setter + def bold(self, value): + self._set_bool_prop('b', value) + + @property + def color(self): + """ + A |ColorFormat| object providing a way to get and set the text color + for this font. + """ + return ColorFormat(self._element) + + @property + def complex_script(self): + """ + Read/write tri-state value. When |True|, causes the characters in the + run to be treated as complex script regardless of their Unicode + values. + """ + return self._get_bool_prop('cs') + + @complex_script.setter + def complex_script(self, value): + self._set_bool_prop('cs', value) + + @property + def cs_bold(self): + """ + Read/write tri-state value. When |True|, causes the complex script + characters in the run to be displayed in bold typeface. + """ + return self._get_bool_prop('bCs') + + @cs_bold.setter + def cs_bold(self, value): + self._set_bool_prop('bCs', value) + + @property + def cs_italic(self): + """ + Read/write tri-state value. When |True|, causes the complex script + characters in the run to be displayed in italic typeface. + """ + return self._get_bool_prop('iCs') + + @cs_italic.setter + def cs_italic(self, value): + self._set_bool_prop('iCs', value) + + @property + def double_strike(self): + """ + Read/write tri-state value. When |True|, causes the text in the run + to appear with double strikethrough. + """ + return self._get_bool_prop('dstrike') + + @double_strike.setter + def double_strike(self, value): + self._set_bool_prop('dstrike', value) + + @property + def emboss(self): + """ + Read/write tri-state value. When |True|, causes the text in the run + to appear as if raised off the page in relief. + """ + return self._get_bool_prop('emboss') + + @emboss.setter + def emboss(self, value): + self._set_bool_prop('emboss', value) + + @property + def hidden(self): + """ + Read/write tri-state value. When |True|, causes the text in the run + to be hidden from display, unless applications settings force hidden + text to be shown. + """ + return self._get_bool_prop('vanish') + + @hidden.setter + def hidden(self, value): + self._set_bool_prop('vanish', value) + + @property + def highlight_color(self): + """ + A member of :ref:`WdColorIndex` indicating the color of highlighting + applied, or `None` if no highlighting is applied. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.highlight_val + + @highlight_color.setter + def highlight_color(self, value): + rPr = self._element.get_or_add_rPr() + rPr.highlight_val = value + + @property + def italic(self): + """ + Read/write tri-state value. When |True|, causes the text of the run + to appear in italics. |None| indicates the effective value is + inherited from the style hierarchy. + """ + return self._get_bool_prop('i') + + @italic.setter + def italic(self, value): + self._set_bool_prop('i', value) + + @property + def imprint(self): + """ + Read/write tri-state value. When |True|, causes the text in the run + to appear as if pressed into the page. + """ + return self._get_bool_prop('imprint') + + @imprint.setter + def imprint(self, value): + self._set_bool_prop('imprint', value) + + @property + def math(self): + """ + Read/write tri-state value. When |True|, specifies this run contains + WML that should be handled as though it was Office Open XML Math. + """ + return self._get_bool_prop('oMath') + + @math.setter + def math(self, value): + self._set_bool_prop('oMath', value) + + @property + def name(self): + """ + Get or set the typeface name for this |Font| instance, causing the + text it controls to appear in the named font, if a matching font is + found. |None| indicates the typeface is inherited from the style + hierarchy. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.rFonts_ascii + + @name.setter + def name(self, value): + rPr = self._element.get_or_add_rPr() + rPr.rFonts_ascii = value + rPr.rFonts_hAnsi = value + + @property + def no_proof(self): + """ + Read/write tri-state value. When |True|, specifies that the contents + of this run should not report any errors when the document is scanned + for spelling and grammar. + """ + return self._get_bool_prop('noProof') + + @no_proof.setter + def no_proof(self, value): + self._set_bool_prop('noProof', value) + + @property + def outline(self): + """ + Read/write tri-state value. When |True| causes the characters in the + run to appear as if they have an outline, by drawing a one pixel wide + border around the inside and outside borders of each character glyph. + """ + return self._get_bool_prop('outline') + + @outline.setter + def outline(self, value): + self._set_bool_prop('outline', value) + + @property + def rtl(self): + """ + Read/write tri-state value. When |True| causes the text in the run + to have right-to-left characteristics. + """ + return self._get_bool_prop('rtl') + + @rtl.setter + def rtl(self, value): + self._set_bool_prop('rtl', value) + + @property + def shadow(self): + """ + Read/write tri-state value. When |True| causes the text in the run + to appear as if each character has a shadow. + """ + return self._get_bool_prop('shadow') + + @shadow.setter + def shadow(self, value): + self._set_bool_prop('shadow', value) + + @property + def size(self): + """ + Read/write |Length| value or |None|, indicating the font height in + English Metric Units (EMU). |None| indicates the font size should be + inherited from the style hierarchy. |Length| is a subclass of |int| + having properties for convenient conversion into points or other + length units. The :class:`docx.shared.Pt` class allows convenient + specification of point values:: + + >> font.size = Pt(24) + >> font.size + 304800 + >> font.size.pt + 24.0 + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.sz_val + + @size.setter + def size(self, emu): + rPr = self._element.get_or_add_rPr() + rPr.sz_val = emu + + @property + def small_caps(self): + """ + Read/write tri-state value. When |True| causes the lowercase + characters in the run to appear as capital letters two points smaller + than the font size specified for the run. + """ + return self._get_bool_prop('smallCaps') + + @small_caps.setter + def small_caps(self, value): + self._set_bool_prop('smallCaps', value) + + @property + def snap_to_grid(self): + """ + Read/write tri-state value. When |True| causes the run to use the + document grid characters per line settings defined in the docGrid + element when laying out the characters in this run. + """ + return self._get_bool_prop('snapToGrid') + + @snap_to_grid.setter + def snap_to_grid(self, value): + self._set_bool_prop('snapToGrid', value) + + @property + def spec_vanish(self): + """ + Read/write tri-state value. When |True|, specifies that the given run + shall always behave as if it is hidden, even when hidden text is + being displayed in the current document. The property has a very + narrow, specialized use related to the table of contents. Consult the + spec (§17.3.2.36) for more details. + """ + return self._get_bool_prop('specVanish') + + @spec_vanish.setter + def spec_vanish(self, value): + self._set_bool_prop('specVanish', value) + + @property + def strike(self): + """ + Read/write tri-state value. When |True| causes the text in the run + to appear with a single horizontal line through the center of the + line. + """ + return self._get_bool_prop('strike') + + @strike.setter + def strike(self, value): + self._set_bool_prop('strike', value) + + @property + def subscript(self): + """ + Boolean indicating whether the characters in this |Font| appear as + subscript. |None| indicates the subscript/subscript value is + inherited from the style hierarchy. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.subscript + + @subscript.setter + def subscript(self, value): + rPr = self._element.get_or_add_rPr() + rPr.subscript = value + + @property + def superscript(self): + """ + Boolean indicating whether the characters in this |Font| appear as + superscript. |None| indicates the subscript/superscript value is + inherited from the style hierarchy. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.superscript + + @superscript.setter + def superscript(self, value): + rPr = self._element.get_or_add_rPr() + rPr.superscript = value + + @property + def underline(self): + """ + The underline style for this |Font|, one of |None|, |True|, |False|, + or a value from :ref:`WdUnderline`. |None| indicates the font + inherits its underline value from the style hierarchy. |False| + indicates no underline. |True| indicates single underline. The values + from :ref:`WdUnderline` are used to specify other outline styles such + as double, wavy, and dotted. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.u_val + + @underline.setter + def underline(self, value): + rPr = self._element.get_or_add_rPr() + rPr.u_val = value + + @property + def web_hidden(self): + """ + Read/write tri-state value. When |True|, specifies that the contents + of this run shall be hidden when the document is displayed in web + page view. + """ + return self._get_bool_prop('webHidden') + + @web_hidden.setter + def web_hidden(self, value): + self._set_bool_prop('webHidden', value) + + def _get_bool_prop(self, name): + """ + Return the value of boolean child of `w:rPr` having *name*. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr._get_bool_val(name) + + def _set_bool_prop(self, name, value): + """ + Assign *value* to the boolean child *name* of `w:rPr`. + """ + rPr = self._element.get_or_add_rPr() + rPr._set_bool_val(name, value) diff --git a/build/lib/docx/text/paragraph.py b/build/lib/docx/text/paragraph.py new file mode 100644 index 000000000..7c9a312ed --- /dev/null +++ b/build/lib/docx/text/paragraph.py @@ -0,0 +1,165 @@ +# encoding: utf-8 + +""" +Paragraph-related proxy types. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..enum.style import WD_STYLE_TYPE +from .parfmt import ParagraphFormat +from .run import Run +from ..shared import Parented + +from datetime import datetime + +class Paragraph(Parented): + """ + Proxy object wrapping ```` element. + """ + def __init__(self, p, parent): + super(Paragraph, self).__init__(parent) + self._p = self._element = p + + def add_run(self, text=None, style=None): + """ + Append a run to this paragraph containing *text* and having character + style identified by style ID *style*. *text* can contain tab + (``\\t``) characters, which are converted to the appropriate XML form + for a tab. *text* can also include newline (``\\n``) or carriage + return (``\\r``) characters, each of which is converted to a line + break. + """ + r = self._p.add_r() + run = Run(r, self) + if text: + run.text = text + if style: + run.style = style + return run + + # def add_comment(self, author, initials, dt, comment_text, rangeStart=0, rangeEnd=0): + # comment_part = self.part.comments_part.element + # comment = comment_part.add_comment(author, initials, dt) + # comment._add_p(comment_text) + + # _r = self._p.add_r() + # _r.add_comment_reference(comment._id) + # self._p.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) + + # return comment + + def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0): + comment_part = self.part._comments_part.element + if dtime is None: + dtime = str( datetime.now ) + comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) + + return comment + + @property + def alignment(self): + """ + A member of the :ref:`WdParagraphAlignment` enumeration specifying + the justification setting for this paragraph. A value of |None| + indicates the paragraph has no directly-applied alignment value and + will inherit its alignment value from its style hierarchy. Assigning + |None| to this property removes any directly-applied alignment value. + """ + return self._p.alignment + + @alignment.setter + def alignment(self, value): + self._p.alignment = value + + def clear(self): + """ + Return this same paragraph after removing all its content. + Paragraph-level formatting, such as style, is preserved. + """ + self._p.clear_content() + return self + + def insert_paragraph_before(self, text=None, style=None): + """ + Return a newly created paragraph, inserted directly before this + paragraph. If *text* is supplied, the new paragraph contains that + text in a single run. If *style* is provided, that style is assigned + to the new paragraph. + """ + paragraph = self._insert_paragraph_before() + if text: + paragraph.add_run(text) + if style is not None: + paragraph.style = style + return paragraph + + @property + def paragraph_format(self): + """ + The |ParagraphFormat| object providing access to the formatting + properties for this paragraph, such as line spacing and indentation. + """ + return ParagraphFormat(self._element) + + @property + def runs(self): + """ + Sequence of |Run| instances corresponding to the elements in + this paragraph. + """ + return [Run(r, self) for r in self._p.r_lst] + + @property + def style(self): + """ + Read/Write. |_ParagraphStyle| object representing the style assigned + to this paragraph. If no explicit style is assigned to this + paragraph, its value is the default paragraph style for the document. + A paragraph style name can be assigned in lieu of a paragraph style + object. Assigning |None| removes any applied style, making its + effective value the default paragraph style for the document. + """ + style_id = self._p.style + return self.part.get_style(style_id, WD_STYLE_TYPE.PARAGRAPH) + + @style.setter + def style(self, style_or_name): + style_id = self.part.get_style_id( + style_or_name, WD_STYLE_TYPE.PARAGRAPH + ) + self._p.style = style_id + + @property + def text(self): + """ + String formed by concatenating the text of each run in the paragraph. + Tabs and line breaks in the XML are mapped to ``\\t`` and ``\\n`` + characters respectively. + + Assigning text to this property causes all existing paragraph content + to be replaced with a single run containing the assigned text. + A ``\\t`` character in the text is mapped to a ```` element + and each ``\\n`` or ``\\r`` character is mapped to a line break. + Paragraph-level formatting, such as style, is preserved. All + run-level formatting, such as bold or italic, is removed. + """ + text = '' + for run in self.runs: + text += run.text + return text + + @text.setter + def text(self, text): + self.clear() + self.add_run(text) + + def _insert_paragraph_before(self): + """ + Return a newly created paragraph, inserted directly before this + paragraph. + """ + p = self._p.add_p_before() + return Paragraph(p, self._parent) diff --git a/build/lib/docx/text/parfmt.py b/build/lib/docx/text/parfmt.py new file mode 100644 index 000000000..37206729c --- /dev/null +++ b/build/lib/docx/text/parfmt.py @@ -0,0 +1,303 @@ +# encoding: utf-8 + +""" +Paragraph-related proxy types. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..enum.text import WD_LINE_SPACING +from ..shared import ElementProxy, Emu, lazyproperty, Length, Pt, Twips +from .tabstops import TabStops + + +class ParagraphFormat(ElementProxy): + """ + Provides access to paragraph formatting such as justification, + indentation, line spacing, space before and after, and widow/orphan + control. + """ + + __slots__ = ('_tab_stops',) + + @property + def alignment(self): + """ + A member of the :ref:`WdParagraphAlignment` enumeration specifying + the justification setting for this paragraph. A value of |None| + indicates paragraph alignment is inherited from the style hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.jc_val + + @alignment.setter + def alignment(self, value): + pPr = self._element.get_or_add_pPr() + pPr.jc_val = value + + @property + def first_line_indent(self): + """ + |Length| value specifying the relative difference in indentation for + the first line of the paragraph. A positive value causes the first + line to be indented. A negative value produces a hanging indent. + |None| indicates first line indentation is inherited from the style + hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.first_line_indent + + @first_line_indent.setter + def first_line_indent(self, value): + pPr = self._element.get_or_add_pPr() + pPr.first_line_indent = value + + @property + def keep_together(self): + """ + |True| if the paragraph should be kept "in one piece" and not broken + across a page boundary when the document is rendered. |None| + indicates its effective value is inherited from the style hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.keepLines_val + + @keep_together.setter + def keep_together(self, value): + self._element.get_or_add_pPr().keepLines_val = value + + @property + def keep_with_next(self): + """ + |True| if the paragraph should be kept on the same page as the + subsequent paragraph when the document is rendered. For example, this + property could be used to keep a section heading on the same page as + its first paragraph. |None| indicates its effective value is + inherited from the style hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.keepNext_val + + @keep_with_next.setter + def keep_with_next(self, value): + self._element.get_or_add_pPr().keepNext_val = value + + @property + def left_indent(self): + """ + |Length| value specifying the space between the left margin and the + left side of the paragraph. |None| indicates the left indent value is + inherited from the style hierarchy. Use an |Inches| value object as + a convenient way to apply indentation in units of inches. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.ind_left + + @left_indent.setter + def left_indent(self, value): + pPr = self._element.get_or_add_pPr() + pPr.ind_left = value + + @property + def line_spacing(self): + """ + |float| or |Length| value specifying the space between baselines in + successive lines of the paragraph. A value of |None| indicates line + spacing is inherited from the style hierarchy. A float value, e.g. + ``2.0`` or ``1.75``, indicates spacing is applied in multiples of + line heights. A |Length| value such as ``Pt(12)`` indicates spacing + is a fixed height. The |Pt| value class is a convenient way to apply + line spacing in units of points. Assigning |None| resets line spacing + to inherit from the style hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return self._line_spacing(pPr.spacing_line, pPr.spacing_lineRule) + + @line_spacing.setter + def line_spacing(self, value): + pPr = self._element.get_or_add_pPr() + if value is None: + pPr.spacing_line = None + pPr.spacing_lineRule = None + elif isinstance(value, Length): + pPr.spacing_line = value + if pPr.spacing_lineRule != WD_LINE_SPACING.AT_LEAST: + pPr.spacing_lineRule = WD_LINE_SPACING.EXACTLY + else: + pPr.spacing_line = Emu(value * Twips(240)) + pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE + + @property + def line_spacing_rule(self): + """ + A member of the :ref:`WdLineSpacing` enumeration indicating how the + value of :attr:`line_spacing` should be interpreted. Assigning any of + the :ref:`WdLineSpacing` members :attr:`SINGLE`, :attr:`DOUBLE`, or + :attr:`ONE_POINT_FIVE` will cause the value of :attr:`line_spacing` + to be updated to produce the corresponding line spacing. + """ + pPr = self._element.pPr + if pPr is None: + return None + return self._line_spacing_rule( + pPr.spacing_line, pPr.spacing_lineRule + ) + + @line_spacing_rule.setter + def line_spacing_rule(self, value): + pPr = self._element.get_or_add_pPr() + if value == WD_LINE_SPACING.SINGLE: + pPr.spacing_line = Twips(240) + pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE + elif value == WD_LINE_SPACING.ONE_POINT_FIVE: + pPr.spacing_line = Twips(360) + pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE + elif value == WD_LINE_SPACING.DOUBLE: + pPr.spacing_line = Twips(480) + pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE + else: + pPr.spacing_lineRule = value + + @property + def page_break_before(self): + """ + |True| if the paragraph should appear at the top of the page + following the prior paragraph. |None| indicates its effective value + is inherited from the style hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.pageBreakBefore_val + + @page_break_before.setter + def page_break_before(self, value): + self._element.get_or_add_pPr().pageBreakBefore_val = value + + @property + def right_indent(self): + """ + |Length| value specifying the space between the right margin and the + right side of the paragraph. |None| indicates the right indent value + is inherited from the style hierarchy. Use a |Cm| value object as + a convenient way to apply indentation in units of centimeters. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.ind_right + + @right_indent.setter + def right_indent(self, value): + pPr = self._element.get_or_add_pPr() + pPr.ind_right = value + + @property + def space_after(self): + """ + |Length| value specifying the spacing to appear between this + paragraph and the subsequent paragraph. |None| indicates this value + is inherited from the style hierarchy. |Length| objects provide + convenience properties, such as :attr:`~.Length.pt` and + :attr:`~.Length.inches`, that allow easy conversion to various length + units. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.spacing_after + + @space_after.setter + def space_after(self, value): + self._element.get_or_add_pPr().spacing_after = value + + @property + def space_before(self): + """ + |Length| value specifying the spacing to appear between this + paragraph and the prior paragraph. |None| indicates this value is + inherited from the style hierarchy. |Length| objects provide + convenience properties, such as :attr:`~.Length.pt` and + :attr:`~.Length.cm`, that allow easy conversion to various length + units. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.spacing_before + + @space_before.setter + def space_before(self, value): + self._element.get_or_add_pPr().spacing_before = value + + @lazyproperty + def tab_stops(self): + """ + |TabStops| object providing access to the tab stops defined for this + paragraph format. + """ + pPr = self._element.get_or_add_pPr() + return TabStops(pPr) + + @property + def widow_control(self): + """ + |True| if the first and last lines in the paragraph remain on the + same page as the rest of the paragraph when Word repaginates the + document. |None| indicates its effective value is inherited from the + style hierarchy. + """ + pPr = self._element.pPr + if pPr is None: + return None + return pPr.widowControl_val + + @widow_control.setter + def widow_control(self, value): + self._element.get_or_add_pPr().widowControl_val = value + + @staticmethod + def _line_spacing(spacing_line, spacing_lineRule): + """ + Return the line spacing value calculated from the combination of + *spacing_line* and *spacing_lineRule*. Returns a |float| number of + lines when *spacing_lineRule* is ``WD_LINE_SPACING.MULTIPLE``, + otherwise a |Length| object of absolute line height is returned. + Returns |None| when *spacing_line* is |None|. + """ + if spacing_line is None: + return None + if spacing_lineRule == WD_LINE_SPACING.MULTIPLE: + return spacing_line / Pt(12) + return spacing_line + + @staticmethod + def _line_spacing_rule(line, lineRule): + """ + Return the line spacing rule value calculated from the combination of + *line* and *lineRule*. Returns special members of the + :ref:`WdLineSpacing` enumeration when line spacing is single, double, + or 1.5 lines. + """ + if lineRule == WD_LINE_SPACING.MULTIPLE: + if line == Twips(240): + return WD_LINE_SPACING.SINGLE + if line == Twips(360): + return WD_LINE_SPACING.ONE_POINT_FIVE + if line == Twips(480): + return WD_LINE_SPACING.DOUBLE + return lineRule diff --git a/build/lib/docx/text/run.py b/build/lib/docx/text/run.py new file mode 100644 index 000000000..97d6da7db --- /dev/null +++ b/build/lib/docx/text/run.py @@ -0,0 +1,191 @@ +# encoding: utf-8 + +""" +Run-related proxy objects for python-docx, Run in particular. +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from ..enum.style import WD_STYLE_TYPE +from ..enum.text import WD_BREAK +from .font import Font +from ..shape import InlineShape +from ..shared import Parented + + +class Run(Parented): + """ + Proxy object wrapping ```` element. Several of the properties on Run + take a tri-state value, |True|, |False|, or |None|. |True| and |False| + correspond to on and off respectively. |None| indicates the property is + not specified directly on the run and its effective value is taken from + the style hierarchy. + """ + def __init__(self, r, parent): + super(Run, self).__init__(parent) + self._r = self._element = self.element = r + + def add_break(self, break_type=WD_BREAK.LINE): + """ + Add a break element of *break_type* to this run. *break_type* can + take the values `WD_BREAK.LINE`, `WD_BREAK.PAGE`, and + `WD_BREAK.COLUMN` where `WD_BREAK` is imported from `docx.enum.text`. + *break_type* defaults to `WD_BREAK.LINE`. + """ + type_, clear = { + WD_BREAK.LINE: (None, None), + WD_BREAK.PAGE: ('page', None), + WD_BREAK.COLUMN: ('column', None), + WD_BREAK.LINE_CLEAR_LEFT: ('textWrapping', 'left'), + WD_BREAK.LINE_CLEAR_RIGHT: ('textWrapping', 'right'), + WD_BREAK.LINE_CLEAR_ALL: ('textWrapping', 'all'), + }[break_type] + br = self._r.add_br() + if type_ is not None: + br.type = type_ + if clear is not None: + br.clear = clear + + def add_picture(self, image_path_or_stream, width=None, height=None): + """ + Return an |InlineShape| instance containing the image identified by + *image_path_or_stream*, added to the end of this run. + *image_path_or_stream* can be a path (a string) or a file-like object + containing a binary image. If neither width nor height is specified, + the picture appears at its native size. If only one is specified, it + is used to compute a scaling factor that is then applied to the + unspecified dimension, preserving the aspect ratio of the image. The + native size of the picture is calculated using the dots-per-inch + (dpi) value specified in the image file, defaulting to 72 dpi if no + value is specified, as is often the case. + """ + inline = self.part.new_pic_inline(image_path_or_stream, width, height) + self._r.add_drawing(inline) + return InlineShape(inline) + + def add_tab(self): + """ + Add a ```` element at the end of the run, which Word + interprets as a tab character. + """ + self._r._add_tab() + + def add_text(self, text): + """ + Returns a newly appended |_Text| object (corresponding to a new + ```` child element) to the run, containing *text*. Compare with + the possibly more friendly approach of assigning text to the + :attr:`Run.text` property. + """ + t = self._r.add_t(text) + return _Text(t) + + @property + def bold(self): + """ + Read/write. Causes the text of the run to appear in bold. + """ + return self.font.bold + + @bold.setter + def bold(self, value): + self.font.bold = value + + def clear(self): + """ + Return reference to this run after removing all its content. All run + formatting is preserved. + """ + self._r.clear_content() + return self + + @property + def font(self): + """ + The |Font| object providing access to the character formatting + properties for this run, such as font name and size. + """ + return Font(self._element) + + @property + def italic(self): + """ + Read/write tri-state value. When |True|, causes the text of the run + to appear in italics. + """ + return self.font.italic + + @italic.setter + def italic(self, value): + self.font.italic = value + + @property + def style(self): + """ + Read/write. A |_CharacterStyle| object representing the character + style applied to this run. The default character style for the + document (often `Default Character Font`) is returned if the run has + no directly-applied character style. Setting this property to |None| + removes any directly-applied character style. + """ + style_id = self._r.style + return self.part.get_style(style_id, WD_STYLE_TYPE.CHARACTER) + + @style.setter + def style(self, style_or_name): + style_id = self.part.get_style_id( + style_or_name, WD_STYLE_TYPE.CHARACTER + ) + self._r.style = style_id + + @property + def text(self): + """ + String formed by concatenating the text equivalent of each run + content child element into a Python string. Each ```` element + adds the text characters it contains. A ```` element adds + a ``\\t`` character. A ```` or ```` element each add + a ``\\n`` character. Note that a ```` element can indicate + a page break or column break as well as a line break. All ```` + elements translate to a single ``\\n`` character regardless of their + type. All other content child elements, such as ````, are + ignored. + + Assigning text to this property has the reverse effect, translating + each ``\\t`` character to a ```` element and each ``\\n`` or + ``\\r`` character to a ```` element. Any existing run content + is replaced. Run formatting is preserved. + """ + return self._r.text + + @text.setter + def text(self, text): + self._r.text = text + + @property + def underline(self): + """ + The underline style for this |Run|, one of |None|, |True|, |False|, + or a value from :ref:`WdUnderline`. A value of |None| indicates the + run has no directly-applied underline value and so will inherit the + underline value of its containing paragraph. Assigning |None| to this + property removes any directly-applied underline value. A value of + |False| indicates a directly-applied setting of no underline, + overriding any inherited value. A value of |True| indicates single + underline. The values from :ref:`WdUnderline` are used to specify + other outline styles such as double, wavy, and dotted. + """ + return self.font.underline + + @underline.setter + def underline(self, value): + self.font.underline = value + + +class _Text(object): + """ + Proxy object wrapping ```` element. + """ + def __init__(self, t_elm): + super(_Text, self).__init__() + self._t = t_elm diff --git a/build/lib/docx/text/tabstops.py b/build/lib/docx/text/tabstops.py new file mode 100644 index 000000000..c22b9bc91 --- /dev/null +++ b/build/lib/docx/text/tabstops.py @@ -0,0 +1,143 @@ +# encoding: utf-8 + +""" +Tabstop-related proxy types. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..shared import ElementProxy +from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER + + +class TabStops(ElementProxy): + """ + A sequence of |TabStop| objects providing access to the tab stops of + a paragraph or paragraph style. Supports iteration, indexed access, del, + and len(). It is accesed using the :attr:`~.ParagraphFormat.tab_stops` + property of ParagraphFormat; it is not intended to be constructed + directly. + """ + + __slots__ = ('_pPr') + + def __init__(self, element): + super(TabStops, self).__init__(element, None) + self._pPr = element + + def __delitem__(self, idx): + """ + Remove the tab at offset *idx* in this sequence. + """ + tabs = self._pPr.tabs + try: + tabs.remove(tabs[idx]) + except (AttributeError, IndexError): + raise IndexError('tab index out of range') + + if len(tabs) == 0: + self._pPr.remove(tabs) + + def __getitem__(self, idx): + """ + Enables list-style access by index. + """ + tabs = self._pPr.tabs + if tabs is None: + raise IndexError('TabStops object is empty') + tab = tabs.tab_lst[idx] + return TabStop(tab) + + def __iter__(self): + """ + Generate a TabStop object for each of the w:tab elements, in XML + document order. + """ + tabs = self._pPr.tabs + if tabs is not None: + for tab in tabs.tab_lst: + yield TabStop(tab) + + def __len__(self): + tabs = self._pPr.tabs + if tabs is None: + return 0 + return len(tabs.tab_lst) + + def add_tab_stop(self, position, alignment=WD_TAB_ALIGNMENT.LEFT, + leader=WD_TAB_LEADER.SPACES): + """ + Add a new tab stop at *position*, a |Length| object specifying the + location of the tab stop relative to the paragraph edge. A negative + *position* value is valid and appears in hanging indentation. Tab + alignment defaults to left, but may be specified by passing a member + of the :ref:`WdTabAlignment` enumeration as *alignment*. An optional + leader character can be specified by passing a member of the + :ref:`WdTabLeader` enumeration as *leader*. + """ + tabs = self._pPr.get_or_add_tabs() + tab = tabs.insert_tab_in_order(position, alignment, leader) + return TabStop(tab) + + def clear_all(self): + """ + Remove all custom tab stops. + """ + self._pPr._remove_tabs() + + +class TabStop(ElementProxy): + """ + An individual tab stop applying to a paragraph or style. Accessed using + list semantics on its containing |TabStops| object. + """ + + __slots__ = ('_tab') + + def __init__(self, element): + super(TabStop, self).__init__(element, None) + self._tab = element + + @property + def alignment(self): + """ + A member of :ref:`WdTabAlignment` specifying the alignment setting + for this tab stop. Read/write. + """ + return self._tab.val + + @alignment.setter + def alignment(self, value): + self._tab.val = value + + @property + def leader(self): + """ + A member of :ref:`WdTabLeader` specifying a repeating character used + as a "leader", filling in the space spanned by this tab. Assigning + |None| produces the same result as assigning `WD_TAB_LEADER.SPACES`. + Read/write. + """ + return self._tab.leader + + @leader.setter + def leader(self, value): + self._tab.leader = value + + @property + def position(self): + """ + A |Length| object representing the distance of this tab stop from the + inside edge of the paragraph. May be positive or negative. + Read/write. + """ + return self._tab.pos + + @position.setter + def position(self, value): + tab = self._tab + tabs = tab.getparent() + self._tab = tabs.insert_tab_in_order(value, tab.val, tab.leader) + tabs.remove(tab) From 1e64f0260d7e68658ad6ef7aba4b8cf0d5ada065 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 28 Feb 2019 11:27:17 +0200 Subject: [PATCH 04/70] acess comment data --- docx/document.py | 2 +- docx/oxml/comments.py | 24 ++++++++++++++++++++++-- docx/oxml/text/paragraph.py | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docx/document.py b/docx/document.py index e87f3cc06..1a5f292a3 100644 --- a/docx/document.py +++ b/docx/document.py @@ -114,7 +114,7 @@ def comments_part(self): A |Comments| object providing read/write access to the core properties of this document. """ - return self._part._package._comments_part + return self.part.comments_part @property diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index c4df76308..b834820e5 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -4,7 +4,9 @@ from . import OxmlElement from .simpletypes import ST_DecimalNumber, ST_String -from docx.text.run import Run +from ..opc.constants import NAMESPACE +from ..text.paragraph import Paragraph +from ..text.run import Run from .xmlchemy import ( BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne ) @@ -18,7 +20,7 @@ class CT_Com(BaseOxmlElement): date = RequiredAttribute('w:date', ST_String) author = RequiredAttribute('w:author', ST_String) - paragraph = ZeroOrOne('w:p', successors=('w:comment',)) + _p = ZeroOrOne('w:p', successors=('w:comment',)) @classmethod def new(cls, initials, comm_id, date, author): @@ -40,6 +42,17 @@ def _add_p(self, text): run.text = text self._insert_paragraph(_p) return _p + + @property + def meta(self): + return [self.author, self.initials, self.date] + + @property + def paragraph(self): + return Paragraph(self._p, self) + + + class CT_Comments(BaseOxmlElement): """ @@ -108,3 +121,10 @@ def new (cls, _id): commentReference = OxmlElement('w:commentReference') commentReference._id =_id return commentReference + + def get_comment_by_id(self, _id): + namesapce = NAMESPACE().WML_MAIN + for c in self.findall('.//w:comment',{'w':namesapce}): + if c._id == _id: + return c + return None \ No newline at end of file diff --git a/docx/oxml/text/paragraph.py b/docx/oxml/text/paragraph.py index 36bb40e81..3421d8007 100644 --- a/docx/oxml/text/paragraph.py +++ b/docx/oxml/text/paragraph.py @@ -103,7 +103,7 @@ def style(self): @property def comment_id(self): _id = self.xpath('./w:commentRangeStart/@w:id') - if(len(_id)>1): + if(len(_id)>1 or len(_id) == 0): return None else: return int(_id[0]) From d5a4aa9605bcc6dfce531a0b572a97112883a2d8 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 28 Feb 2019 12:01:22 +0200 Subject: [PATCH 05/70] fix a bug /oxml/comments.py --- docx/oxml/comments.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index b834820e5..f7f4baefe 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -79,6 +79,13 @@ def _next_commentId(self): return _ids[-1] + 2 except: return 0 + + def get_comment_by_id(self, _id): + namesapce = NAMESPACE().WML_MAIN + for c in self.findall('.//w:comment',{'w':namesapce}): + if c._id == _id: + return c + return None class CT_CRS(BaseOxmlElement): @@ -122,9 +129,4 @@ def new (cls, _id): commentReference._id =_id return commentReference - def get_comment_by_id(self, _id): - namesapce = NAMESPACE().WML_MAIN - for c in self.findall('.//w:comment',{'w':namesapce}): - if c._id == _id: - return c - return None \ No newline at end of file + From 2066e17afb5af7aa06c9a6369d2c6f13ccb5f5c4 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 28 Feb 2019 12:48:56 +0200 Subject: [PATCH 06/70] mini bug --- docx/oxml/comments.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index f7f4baefe..65082b4f0 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -20,7 +20,7 @@ class CT_Com(BaseOxmlElement): date = RequiredAttribute('w:date', ST_String) author = RequiredAttribute('w:author', ST_String) - _p = ZeroOrOne('w:p', successors=('w:comment',)) + p = ZeroOrOne('w:p', successors=('w:comment',)) @classmethod def new(cls, initials, comm_id, date, author): @@ -40,7 +40,7 @@ def _add_p(self, text): _r = _p.add_r() run = Run(_r,self) run.text = text - self._insert_paragraph(_p) + self._insert_p(_p) return _p @property @@ -101,8 +101,6 @@ def new(cls, _id): return commentRangeStart - - class CT_CRE(BaseOxmlElement): """ A ``w:commentRangeEnd`` element From ecb2ee522e427bca21e6eb3561c631651759149a Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 28 Feb 2019 12:49:57 +0200 Subject: [PATCH 07/70] mini bug --- docx/oxml/comments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index 65082b4f0..4fb0d6dfc 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -49,7 +49,7 @@ def meta(self): @property def paragraph(self): - return Paragraph(self._p, self) + return Paragraph(self.p, self) From 827a5bf20df74d3a02c1cc56a2eb1505c7029ab3 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 1 Mar 2019 15:57:10 +0200 Subject: [PATCH 08/70] low level support for footnotes --- docx/__init__.py | 4 +- docx/document.py | 9 +++ docx/opc/package.py | 14 +++++ docx/oxml/__init__.py | 11 +++- docx/oxml/comments.py | 1 - docx/oxml/footnotes.py | 89 ++++++++++++++++++++++++++++ docx/oxml/text/paragraph.py | 24 +++++++- docx/oxml/text/run.py | 37 +++++++++++- docx/parts/document.py | 16 ++++- docx/parts/footnotes.py | 26 ++++++++ docx/templates/default-footnotes.xml | 6 ++ docx/text/paragraph.py | 6 ++ 12 files changed, 235 insertions(+), 8 deletions(-) create mode 100644 docx/oxml/footnotes.py create mode 100644 docx/parts/footnotes.py create mode 100644 docx/templates/default-footnotes.xml diff --git a/docx/__init__.py b/docx/__init__.py index 37205dc54..59bc452ae 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -17,6 +17,7 @@ from docx.parts.settings import SettingsPart from docx.parts.styles import StylesPart from docx.parts.comments import CommentsPart +from docx.parts.footnotes import FootnotesPart def part_class_selector(content_type, reltype): @@ -32,8 +33,9 @@ def part_class_selector(content_type, reltype): PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart PartFactory.part_type_for[CT.WML_STYLES] = StylesPart +PartFactory.part_type_for[CT.WML_FOOTNOTES] = FootnotesPart del ( - CT, CorePropertiesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, + CT, CorePropertiesPart, FootnotesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, StylesPart, part_class_selector, ) diff --git a/docx/document.py b/docx/document.py index 1a5f292a3..71f5f1149 100644 --- a/docx/document.py +++ b/docx/document.py @@ -115,6 +115,15 @@ def comments_part(self): properties of this document. """ return self.part.comments_part + + @property + def footnotes_part(self): + """ + A |Footnotes| object providing read/write access to the core + properties of this document. + """ + return self.part.footnotes_part + @property diff --git a/docx/opc/package.py b/docx/opc/package.py index 5b817274f..4951c2319 100644 --- a/docx/opc/package.py +++ b/docx/opc/package.py @@ -12,6 +12,7 @@ from .part import PartFactory from .parts.coreprops import CorePropertiesPart from docx.parts.comments import CommentsPart +from ..parts.footnotes import FootnotesPart from .pkgreader import PackageReader from .pkgwriter import PackageWriter from .rel import Relationships @@ -186,6 +187,19 @@ def _comments_part(self): self.relate_to(comments_part, RT.COMMENTS) return comments_part + @property + def _footnotes_part(self): + """ + |FootnotesPart| object related to this package. Creates + a default Comments part if one is not present. + """ + try: + return self.part_related_by(RT.FOOTNOTES) + except KeyError: + footnotes_part = FootnotesPart.default(self) + self.relate_to(footnotes_part, RT.FOOTNOTES) + return footnotes_part + class Unmarshaller(object): """ diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index fc6e18abc..0560633ab 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -203,6 +203,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:br', CT_Br) register_element_cls('w:r', CT_R) register_element_cls('w:t', CT_Text) +register_element_cls('w:rPr', CT_RPr) from .comments import CT_Comments,CT_Com, CT_CRE, CT_CRS, CT_CRef @@ -210,4 +211,12 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:comment', CT_Com) register_element_cls('w:commentRangeStart', CT_CRS) register_element_cls('w:commentRangeEnd', CT_CRE) -register_element_cls('w:commentReference', CT_CRef) \ No newline at end of file +register_element_cls('w:commentReference', CT_CRef) + + +from .footnotes import CT_Footnotes, CT_Footnote, CT_FNR, CT_FootnoteRef + +register_element_cls('w:footnotes', CT_Footnotes) +register_element_cls('w:footnote', CT_Footnote) +register_element_cls('w:footnoteReference', CT_FNR) +register_element_cls('w:footnoteRef', CT_FootnoteRef) \ No newline at end of file diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index 4fb0d6dfc..49afde5ce 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -74,7 +74,6 @@ def _next_commentId(self): _ids = [int(_str) for _str in ids] _ids.sort() - print(_ids) try: return _ids[-1] + 2 except: diff --git a/docx/oxml/footnotes.py b/docx/oxml/footnotes.py new file mode 100644 index 000000000..90c791bc3 --- /dev/null +++ b/docx/oxml/footnotes.py @@ -0,0 +1,89 @@ +""" +Custom element classes related to the footnotes part +""" + + +from . import OxmlElement +from .simpletypes import ST_DecimalNumber, ST_String +from ..text.paragraph import Paragraph +from ..text.run import Run +from ..opc.constants import NAMESPACE +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne +) + + +class CT_Footnotes(BaseOxmlElement): + """ + A ```` element, a container for Footnotes properties + """ + + footnote = ZeroOrMore ('w:footnote', successors=('w:footnotes',)) + + @property + def _next_id(self): + ids = self.xpath('./w:footnote/@w:id') + + return int(ids[-1]) + 1 + + def add_footnote(self): + _next_id = self._next_id + footnote = CT_Footnote.new(_next_id) + footnote = self._insert_footnote(footnote) + return footnote + + def get_footnote_by_id(self, _id): + namesapce = NAMESPACE().WML_MAIN + for fn in self.findall('.//w:footnote', {'w':namesapce}): + if fn._id == _id: + return fn + return None + +class CT_Footnote(BaseOxmlElement): + """ + A ```` element, a container for Footnote properties + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + p = ZeroOrOne('w:p', successors=('w:footnote',)) + + @classmethod + def new(cls, _id): + footnote = OxmlElement('w:footnote') + footnote._id = _id + + return footnote + + def _add_p(self, text): + _p = OxmlElement('w:p') + _p.footnote_style() + + _r = _p.add_r() + _r.footnote_style() + _r = _p.add_r() + _r.add_footnoteRef() + + run = Run(_r, self) + run.text = text + + self._insert_p(_p) + return _p + + @property + def paragraph(self): + return Paragraph(self.p, self) + +class CT_FNR(BaseOxmlElement): + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + @classmethod + def new (cls, _id): + footnoteReference = OxmlElement('w:footnoteReference') + footnoteReference._id = _id + return footnoteReference + +class CT_FootnoteRef (BaseOxmlElement): + + @classmethod + def new (cls): + ref = OxmlElement('w:footnoteRef') + return ref \ No newline at end of file diff --git a/docx/oxml/text/paragraph.py b/docx/oxml/text/paragraph.py index 3421d8007..6c32f43c0 100644 --- a/docx/oxml/text/paragraph.py +++ b/docx/oxml/text/paragraph.py @@ -52,8 +52,20 @@ def add_comm(self, author, comment_part, initials, dtime, comment_text, rangeSta return comment - + def add_fn(self, text, footnotes_part): + footnote = footnotes_part.add_footnote() + footnote._add_p(' '+text) + _r = self.add_r() + _r.add_footnote_reference(footnote._id) + return footnote + + def footnote_style(self): + pPr = self.get_or_add_pPr() + rstyle = pPr.get_or_add_pStyle() + rstyle.val = 'FootnoteText' + + return self @property def alignment(self): @@ -103,12 +115,20 @@ def style(self): @property def comment_id(self): _id = self.xpath('./w:commentRangeStart/@w:id') - if(len(_id)>1 or len(_id) == 0): + if len(_id) > 1 or len(_id) == 0: return None else: return int(_id[0]) + @property + def footnote_id(self): + _id = self.xpath('./w:r/w:footnoteReference/@w:id') + if len(_id) > 1 or len(_id) == 0 : + return None + else: + return int(_id[0]) + @style.setter def style(self, style): pPr = self.get_or_add_pPr() diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index 010eb2935..2a213472b 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -5,10 +5,11 @@ """ from ..ns import qn -from ..simpletypes import ST_BrClear, ST_BrType +from ..simpletypes import ST_BrClear, ST_BrType, ST_DecimalNumber, ST_String + from .. import OxmlElement from ..xmlchemy import ( - BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne + BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne ,RequiredAttribute ) from .. import OxmlElement @@ -63,6 +64,29 @@ def add_comment_reference(self, _id): self.append(reference) return reference + def add_footnote_reference(self, _id): + rPr = self.get_or_add_rPr() + rstyle = rPr.get_or_add_rStyle() + rstyle.val = 'FootnoteReference' + reference = OxmlElement('w:footnoteReference') + reference._id = _id + self.append(reference) + return reference + + def add_footnoteRef(self): + ref = OxmlElement('w:footnoteRef') + self.append(ref) + + return ref + + def footnote_style(self): + rPr = self.get_or_add_rPr() + rstyle = rPr.get_or_add_rStyle() + rstyle.val = 'FootnoteReference' + + self.add_footnoteRef() + return self + def clear_content(self): """ Remove all child elements except the ```` element if present. @@ -127,6 +151,13 @@ class CT_Text(BaseOxmlElement): """ +class CT_RPr(BaseOxmlElement): + rStyle = ZeroOrOne('w:rStyle') + + +class CT_RStyle(BaseOxmlElement): + val = RequiredAttribute('w:val',ST_String) + class _RunContentAppender(object): """ Service object that knows how to translate a Python string into run @@ -181,3 +212,5 @@ def flush(self): if text: self._r.add_t(text) del self._bfr[:] + + diff --git a/docx/parts/document.py b/docx/parts/document.py index a10b1e690..647f72119 100644 --- a/docx/parts/document.py +++ b/docx/parts/document.py @@ -18,6 +18,7 @@ from .settings import SettingsPart from .styles import StylesPart from .comments import CommentsPart +from .footnotes import FootnotesPart class DocumentPart(XmlPart): @@ -189,4 +190,17 @@ def _comments_part(self): except KeyError: comments_part = CommentsPart.default(self) self.relate_to(comments_part, RT.COMMENTS) - return comments_part \ No newline at end of file + return comments_part + + @property + def _footnotes_part(self): + """ + |FootnotesPart| object related to this package. Creates + a default Comments part if one is not present. + """ + try: + return self.part_related_by(RT.FOOTNOTES) + except KeyError: + footnotes_part = FootnotesPart.default(self) + self.relate_to(footnotes_part, RT.FOOTNOTES) + return footnotes_part \ No newline at end of file diff --git a/docx/parts/footnotes.py b/docx/parts/footnotes.py new file mode 100644 index 000000000..67f29fb71 --- /dev/null +++ b/docx/parts/footnotes.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +from ..opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI +from ..opc.part import XmlPart +from ..oxml import parse_xml + +import os + +class FootnotesPart(XmlPart): + """ + Definition of Footnotes Part + """ + @classmethod + def default(cls, package): + partname = PackURI("/word/footnotes.xml") + content_type = CT.WML_FOOTNOTES + element = parse_xml(cls._default_footnotes_xml()) + return cls(partname, content_type, element, package) + + @classmethod + def _default_footnotes_xml(cls): + path = os.path.join(os.path.split(__file__)[0], '..', 'templates', 'default-footnotes.xml') + with open(path, 'rb') as f: + xml_bytes = f.read() + return xml_bytes \ No newline at end of file diff --git a/docx/templates/default-footnotes.xml b/docx/templates/default-footnotes.xml new file mode 100644 index 000000000..5dc12e66f --- /dev/null +++ b/docx/templates/default-footnotes.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 7c9a312ed..4a04c0687 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -58,6 +58,12 @@ def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,ran comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) return comment + + def add_footnote(self, text): + footnotes_part = self.part._footnotes_part.element + footnote = self._p.add_fn(text, footnotes_part) + + return footnote @property def alignment(self): From 64d3dfcbf62f489bca51b73bc8a1736005b5690e Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 1 Mar 2019 19:37:31 +0200 Subject: [PATCH 09/70] footnote id on run element --- build/lib/docx/__init__.py | 4 +- build/lib/docx/document.py | 11 ++- build/lib/docx/opc/package.py | 14 +++ build/lib/docx/oxml/__init__.py | 11 ++- build/lib/docx/oxml/comments.py | 31 +++++-- build/lib/docx/oxml/footnotes.py | 89 +++++++++++++++++++ build/lib/docx/oxml/text/paragraph.py | 24 ++++- build/lib/docx/oxml/text/run.py | 45 +++++++++- build/lib/docx/parts/document.py | 16 +++- build/lib/docx/parts/footnotes.py | 26 ++++++ .../lib/docx/templates/default-footnotes.xml | 6 ++ build/lib/docx/text/paragraph.py | 6 ++ docx/oxml/text/run.py | 8 ++ 13 files changed, 277 insertions(+), 14 deletions(-) create mode 100644 build/lib/docx/oxml/footnotes.py create mode 100644 build/lib/docx/parts/footnotes.py create mode 100644 build/lib/docx/templates/default-footnotes.xml diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py index 37205dc54..59bc452ae 100644 --- a/build/lib/docx/__init__.py +++ b/build/lib/docx/__init__.py @@ -17,6 +17,7 @@ from docx.parts.settings import SettingsPart from docx.parts.styles import StylesPart from docx.parts.comments import CommentsPart +from docx.parts.footnotes import FootnotesPart def part_class_selector(content_type, reltype): @@ -32,8 +33,9 @@ def part_class_selector(content_type, reltype): PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart PartFactory.part_type_for[CT.WML_STYLES] = StylesPart +PartFactory.part_type_for[CT.WML_FOOTNOTES] = FootnotesPart del ( - CT, CorePropertiesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, + CT, CorePropertiesPart, FootnotesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, StylesPart, part_class_selector, ) diff --git a/build/lib/docx/document.py b/build/lib/docx/document.py index e87f3cc06..71f5f1149 100644 --- a/build/lib/docx/document.py +++ b/build/lib/docx/document.py @@ -114,7 +114,16 @@ def comments_part(self): A |Comments| object providing read/write access to the core properties of this document. """ - return self._part._package._comments_part + return self.part.comments_part + + @property + def footnotes_part(self): + """ + A |Footnotes| object providing read/write access to the core + properties of this document. + """ + return self.part.footnotes_part + @property diff --git a/build/lib/docx/opc/package.py b/build/lib/docx/opc/package.py index 5b817274f..4951c2319 100644 --- a/build/lib/docx/opc/package.py +++ b/build/lib/docx/opc/package.py @@ -12,6 +12,7 @@ from .part import PartFactory from .parts.coreprops import CorePropertiesPart from docx.parts.comments import CommentsPart +from ..parts.footnotes import FootnotesPart from .pkgreader import PackageReader from .pkgwriter import PackageWriter from .rel import Relationships @@ -186,6 +187,19 @@ def _comments_part(self): self.relate_to(comments_part, RT.COMMENTS) return comments_part + @property + def _footnotes_part(self): + """ + |FootnotesPart| object related to this package. Creates + a default Comments part if one is not present. + """ + try: + return self.part_related_by(RT.FOOTNOTES) + except KeyError: + footnotes_part = FootnotesPart.default(self) + self.relate_to(footnotes_part, RT.FOOTNOTES) + return footnotes_part + class Unmarshaller(object): """ diff --git a/build/lib/docx/oxml/__init__.py b/build/lib/docx/oxml/__init__.py index fc6e18abc..0560633ab 100644 --- a/build/lib/docx/oxml/__init__.py +++ b/build/lib/docx/oxml/__init__.py @@ -203,6 +203,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:br', CT_Br) register_element_cls('w:r', CT_R) register_element_cls('w:t', CT_Text) +register_element_cls('w:rPr', CT_RPr) from .comments import CT_Comments,CT_Com, CT_CRE, CT_CRS, CT_CRef @@ -210,4 +211,12 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:comment', CT_Com) register_element_cls('w:commentRangeStart', CT_CRS) register_element_cls('w:commentRangeEnd', CT_CRE) -register_element_cls('w:commentReference', CT_CRef) \ No newline at end of file +register_element_cls('w:commentReference', CT_CRef) + + +from .footnotes import CT_Footnotes, CT_Footnote, CT_FNR, CT_FootnoteRef + +register_element_cls('w:footnotes', CT_Footnotes) +register_element_cls('w:footnote', CT_Footnote) +register_element_cls('w:footnoteReference', CT_FNR) +register_element_cls('w:footnoteRef', CT_FootnoteRef) \ No newline at end of file diff --git a/build/lib/docx/oxml/comments.py b/build/lib/docx/oxml/comments.py index c4df76308..49afde5ce 100644 --- a/build/lib/docx/oxml/comments.py +++ b/build/lib/docx/oxml/comments.py @@ -4,7 +4,9 @@ from . import OxmlElement from .simpletypes import ST_DecimalNumber, ST_String -from docx.text.run import Run +from ..opc.constants import NAMESPACE +from ..text.paragraph import Paragraph +from ..text.run import Run from .xmlchemy import ( BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne ) @@ -18,7 +20,7 @@ class CT_Com(BaseOxmlElement): date = RequiredAttribute('w:date', ST_String) author = RequiredAttribute('w:author', ST_String) - paragraph = ZeroOrOne('w:p', successors=('w:comment',)) + p = ZeroOrOne('w:p', successors=('w:comment',)) @classmethod def new(cls, initials, comm_id, date, author): @@ -38,8 +40,19 @@ def _add_p(self, text): _r = _p.add_r() run = Run(_r,self) run.text = text - self._insert_paragraph(_p) + self._insert_p(_p) return _p + + @property + def meta(self): + return [self.author, self.initials, self.date] + + @property + def paragraph(self): + return Paragraph(self.p, self) + + + class CT_Comments(BaseOxmlElement): """ @@ -61,11 +74,17 @@ def _next_commentId(self): _ids = [int(_str) for _str in ids] _ids.sort() - print(_ids) try: return _ids[-1] + 2 except: return 0 + + def get_comment_by_id(self, _id): + namesapce = NAMESPACE().WML_MAIN + for c in self.findall('.//w:comment',{'w':namesapce}): + if c._id == _id: + return c + return None class CT_CRS(BaseOxmlElement): @@ -81,8 +100,6 @@ def new(cls, _id): return commentRangeStart - - class CT_CRE(BaseOxmlElement): """ A ``w:commentRangeEnd`` element @@ -108,3 +125,5 @@ def new (cls, _id): commentReference = OxmlElement('w:commentReference') commentReference._id =_id return commentReference + + diff --git a/build/lib/docx/oxml/footnotes.py b/build/lib/docx/oxml/footnotes.py new file mode 100644 index 000000000..90c791bc3 --- /dev/null +++ b/build/lib/docx/oxml/footnotes.py @@ -0,0 +1,89 @@ +""" +Custom element classes related to the footnotes part +""" + + +from . import OxmlElement +from .simpletypes import ST_DecimalNumber, ST_String +from ..text.paragraph import Paragraph +from ..text.run import Run +from ..opc.constants import NAMESPACE +from .xmlchemy import ( + BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne +) + + +class CT_Footnotes(BaseOxmlElement): + """ + A ```` element, a container for Footnotes properties + """ + + footnote = ZeroOrMore ('w:footnote', successors=('w:footnotes',)) + + @property + def _next_id(self): + ids = self.xpath('./w:footnote/@w:id') + + return int(ids[-1]) + 1 + + def add_footnote(self): + _next_id = self._next_id + footnote = CT_Footnote.new(_next_id) + footnote = self._insert_footnote(footnote) + return footnote + + def get_footnote_by_id(self, _id): + namesapce = NAMESPACE().WML_MAIN + for fn in self.findall('.//w:footnote', {'w':namesapce}): + if fn._id == _id: + return fn + return None + +class CT_Footnote(BaseOxmlElement): + """ + A ```` element, a container for Footnote properties + """ + _id = RequiredAttribute('w:id', ST_DecimalNumber) + p = ZeroOrOne('w:p', successors=('w:footnote',)) + + @classmethod + def new(cls, _id): + footnote = OxmlElement('w:footnote') + footnote._id = _id + + return footnote + + def _add_p(self, text): + _p = OxmlElement('w:p') + _p.footnote_style() + + _r = _p.add_r() + _r.footnote_style() + _r = _p.add_r() + _r.add_footnoteRef() + + run = Run(_r, self) + run.text = text + + self._insert_p(_p) + return _p + + @property + def paragraph(self): + return Paragraph(self.p, self) + +class CT_FNR(BaseOxmlElement): + _id = RequiredAttribute('w:id', ST_DecimalNumber) + + @classmethod + def new (cls, _id): + footnoteReference = OxmlElement('w:footnoteReference') + footnoteReference._id = _id + return footnoteReference + +class CT_FootnoteRef (BaseOxmlElement): + + @classmethod + def new (cls): + ref = OxmlElement('w:footnoteRef') + return ref \ No newline at end of file diff --git a/build/lib/docx/oxml/text/paragraph.py b/build/lib/docx/oxml/text/paragraph.py index 36bb40e81..6c32f43c0 100644 --- a/build/lib/docx/oxml/text/paragraph.py +++ b/build/lib/docx/oxml/text/paragraph.py @@ -52,8 +52,20 @@ def add_comm(self, author, comment_part, initials, dtime, comment_text, rangeSta return comment - + def add_fn(self, text, footnotes_part): + footnote = footnotes_part.add_footnote() + footnote._add_p(' '+text) + _r = self.add_r() + _r.add_footnote_reference(footnote._id) + return footnote + + def footnote_style(self): + pPr = self.get_or_add_pPr() + rstyle = pPr.get_or_add_pStyle() + rstyle.val = 'FootnoteText' + + return self @property def alignment(self): @@ -103,12 +115,20 @@ def style(self): @property def comment_id(self): _id = self.xpath('./w:commentRangeStart/@w:id') - if(len(_id)>1): + if len(_id) > 1 or len(_id) == 0: return None else: return int(_id[0]) + @property + def footnote_id(self): + _id = self.xpath('./w:r/w:footnoteReference/@w:id') + if len(_id) > 1 or len(_id) == 0 : + return None + else: + return int(_id[0]) + @style.setter def style(self, style): pPr = self.get_or_add_pPr() diff --git a/build/lib/docx/oxml/text/run.py b/build/lib/docx/oxml/text/run.py index 010eb2935..a37709e96 100644 --- a/build/lib/docx/oxml/text/run.py +++ b/build/lib/docx/oxml/text/run.py @@ -5,10 +5,11 @@ """ from ..ns import qn -from ..simpletypes import ST_BrClear, ST_BrType +from ..simpletypes import ST_BrClear, ST_BrType, ST_DecimalNumber, ST_String + from .. import OxmlElement from ..xmlchemy import ( - BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne + BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne ,RequiredAttribute ) from .. import OxmlElement @@ -63,6 +64,37 @@ def add_comment_reference(self, _id): self.append(reference) return reference + def add_footnote_reference(self, _id): + rPr = self.get_or_add_rPr() + rstyle = rPr.get_or_add_rStyle() + rstyle.val = 'FootnoteReference' + reference = OxmlElement('w:footnoteReference') + reference._id = _id + self.append(reference) + return reference + + def add_footnoteRef(self): + ref = OxmlElement('w:footnoteRef') + self.append(ref) + + return ref + + def footnote_style(self): + rPr = self.get_or_add_rPr() + rstyle = rPr.get_or_add_rStyle() + rstyle.val = 'FootnoteReference' + + self.add_footnoteRef() + return self + + @property + def footnote_id(self): + _id = self.xpath('./w:footnoteReference/@w:id') + if len(_id) > 1 or len(_id) == 0 : + return None + else: + return int(_id[0]) + def clear_content(self): """ Remove all child elements except the ```` element if present. @@ -127,6 +159,13 @@ class CT_Text(BaseOxmlElement): """ +class CT_RPr(BaseOxmlElement): + rStyle = ZeroOrOne('w:rStyle') + + +class CT_RStyle(BaseOxmlElement): + val = RequiredAttribute('w:val',ST_String) + class _RunContentAppender(object): """ Service object that knows how to translate a Python string into run @@ -181,3 +220,5 @@ def flush(self): if text: self._r.add_t(text) del self._bfr[:] + + diff --git a/build/lib/docx/parts/document.py b/build/lib/docx/parts/document.py index a10b1e690..647f72119 100644 --- a/build/lib/docx/parts/document.py +++ b/build/lib/docx/parts/document.py @@ -18,6 +18,7 @@ from .settings import SettingsPart from .styles import StylesPart from .comments import CommentsPart +from .footnotes import FootnotesPart class DocumentPart(XmlPart): @@ -189,4 +190,17 @@ def _comments_part(self): except KeyError: comments_part = CommentsPart.default(self) self.relate_to(comments_part, RT.COMMENTS) - return comments_part \ No newline at end of file + return comments_part + + @property + def _footnotes_part(self): + """ + |FootnotesPart| object related to this package. Creates + a default Comments part if one is not present. + """ + try: + return self.part_related_by(RT.FOOTNOTES) + except KeyError: + footnotes_part = FootnotesPart.default(self) + self.relate_to(footnotes_part, RT.FOOTNOTES) + return footnotes_part \ No newline at end of file diff --git a/build/lib/docx/parts/footnotes.py b/build/lib/docx/parts/footnotes.py new file mode 100644 index 000000000..67f29fb71 --- /dev/null +++ b/build/lib/docx/parts/footnotes.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +from ..opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI +from ..opc.part import XmlPart +from ..oxml import parse_xml + +import os + +class FootnotesPart(XmlPart): + """ + Definition of Footnotes Part + """ + @classmethod + def default(cls, package): + partname = PackURI("/word/footnotes.xml") + content_type = CT.WML_FOOTNOTES + element = parse_xml(cls._default_footnotes_xml()) + return cls(partname, content_type, element, package) + + @classmethod + def _default_footnotes_xml(cls): + path = os.path.join(os.path.split(__file__)[0], '..', 'templates', 'default-footnotes.xml') + with open(path, 'rb') as f: + xml_bytes = f.read() + return xml_bytes \ No newline at end of file diff --git a/build/lib/docx/templates/default-footnotes.xml b/build/lib/docx/templates/default-footnotes.xml new file mode 100644 index 000000000..5dc12e66f --- /dev/null +++ b/build/lib/docx/templates/default-footnotes.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build/lib/docx/text/paragraph.py b/build/lib/docx/text/paragraph.py index 7c9a312ed..4a04c0687 100644 --- a/build/lib/docx/text/paragraph.py +++ b/build/lib/docx/text/paragraph.py @@ -58,6 +58,12 @@ def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,ran comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) return comment + + def add_footnote(self, text): + footnotes_part = self.part._footnotes_part.element + footnote = self._p.add_fn(text, footnotes_part) + + return footnote @property def alignment(self): diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index 2a213472b..a37709e96 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -86,6 +86,14 @@ def footnote_style(self): self.add_footnoteRef() return self + + @property + def footnote_id(self): + _id = self.xpath('./w:footnoteReference/@w:id') + if len(_id) > 1 or len(_id) == 0 : + return None + else: + return int(_id[0]) def clear_content(self): """ From 76612cabf09d0d63506fec844fc991eb6f2d1a3e Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sun, 3 Mar 2019 13:01:28 +0200 Subject: [PATCH 10/70] fixes --- docx/document.py | 2 +- docx/oxml/text/paragraph.py | 6 +++--- docx/text/paragraph.py | 7 +++++++ docx/text/run.py | 11 +++++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docx/document.py b/docx/document.py index 71f5f1149..3747f0e36 100644 --- a/docx/document.py +++ b/docx/document.py @@ -122,7 +122,7 @@ def footnotes_part(self): A |Footnotes| object providing read/write access to the core properties of this document. """ - return self.part.footnotes_part + return self.part._footnotes_part diff --git a/docx/oxml/text/paragraph.py b/docx/oxml/text/paragraph.py index 6c32f43c0..122b65c5f 100644 --- a/docx/oxml/text/paragraph.py +++ b/docx/oxml/text/paragraph.py @@ -121,12 +121,12 @@ def comment_id(self): return int(_id[0]) @property - def footnote_id(self): + def footnote_ids(self): _id = self.xpath('./w:r/w:footnoteReference/@w:id') - if len(_id) > 1 or len(_id) == 0 : + if len(_id) == 0 : return None else: - return int(_id[0]) + return _id @style.setter diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 4a04c0687..eb8e3f66f 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -157,6 +157,13 @@ def text(self): text += run.text return text + @property + def footnotes(self): + if self._p.footnote_ids is not None : + return True + else : + return False + @text.setter def text(self, text): self.clear() diff --git a/docx/text/run.py b/docx/text/run.py index 97d6da7db..8f2ea787a 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -180,6 +180,17 @@ def underline(self): @underline.setter def underline(self, value): self.font.underline = value + + @property + def footnote(self): + _id = self._r.footnote_id + + if _id is not None: + footnotes_part = self._parent._parent.part._footnotes_part.element + footnote = footnotes_part.get_footnote_by_id(_id) + return footnote.paragraph.text + else: + return None class _Text(object): From b0f244261a5c82388f30d209eae0ac60ddda5e14 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Mon, 4 Mar 2019 17:45:14 +0200 Subject: [PATCH 11/70] added wheel --- python_docx-0.8.7-py3-none-any.whl | Bin 0 -> 247343 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 python_docx-0.8.7-py3-none-any.whl diff --git a/python_docx-0.8.7-py3-none-any.whl b/python_docx-0.8.7-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..cabbf44354fa2fbacdcc9967e5d2c2750d260e21 GIT binary patch literal 247343 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 sU__zbZ%2|{~fMIsE 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 z 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

  • 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�D5z$rn2lGL1R#pOL$(-7#1t&E1K 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 z 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 literal 0 HcmV?d00001 From 8098ef8bb08025b178c243bbeec42b5eb853dd5b Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 7 Mar 2019 23:42:21 +0200 Subject: [PATCH 12/70] updated HISTORY.rst --- HISTORY.rst | 19 ++++++++++++++++++- LICENSE | 3 ++- build/lib/docx/__init__.py | 2 +- build/lib/docx/document.py | 14 +++++++------- build/lib/docx/oxml/text/paragraph.py | 6 +++--- build/lib/docx/text/paragraph.py | 7 +++++++ build/lib/docx/text/run.py | 11 +++++++++++ docx/__init__.py | 2 +- docx/document.py | 14 +++++++------- setup.py | 8 ++++---- 10 files changed, 61 insertions(+), 25 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 1f6514d7e..d77fd583c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,23 @@ .. :changelog: -Release History + +Release History BayooG/bayooo-docx forked from (python-openxmm/python-docx) +--------------- + +0.1.2 (2019-03-07) +++++++++++++++++++ + +- Add low-level implementation for comments part +- Add oxml element for element and sub-elements +- Add add_comment() method for docx.text.Paragraph + + +- Add low-level implementation for footnotes part +- Add oxml element for element and sub-elements +- Add add_footnote() method for docx.text.Paragraph + + +Release History python-openxmm/python-docx --------------- 0.8.7 (2018-08-18) diff --git a/LICENSE b/LICENSE index 67ebe716e..dece2863c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Steve Canny, https://github.com/scanny +Copyright (c) 2019 Obay Daba, https://github.com/bayoog +forked from https://github.com/python-openxml/python-docx Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py index 59bc452ae..5a5a4ff42 100644 --- a/build/lib/docx/__init__.py +++ b/build/lib/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.8.7' +__version__ = '0.1.2' # register custom Part classes with opc package reader diff --git a/build/lib/docx/document.py b/build/lib/docx/document.py index 71f5f1149..f3b2e689f 100644 --- a/build/lib/docx/document.py +++ b/build/lib/docx/document.py @@ -116,13 +116,13 @@ def comments_part(self): """ return self.part.comments_part - @property - def footnotes_part(self): - """ - A |Footnotes| object providing read/write access to the core - properties of this document. - """ - return self.part.footnotes_part + # @property + # def footnotes_part(self): + # """ + # A |Footnotes| object providing read/write access to the core + # properties of this document. + # """ + # return self.part._footnotes_part diff --git a/build/lib/docx/oxml/text/paragraph.py b/build/lib/docx/oxml/text/paragraph.py index 6c32f43c0..122b65c5f 100644 --- a/build/lib/docx/oxml/text/paragraph.py +++ b/build/lib/docx/oxml/text/paragraph.py @@ -121,12 +121,12 @@ def comment_id(self): return int(_id[0]) @property - def footnote_id(self): + def footnote_ids(self): _id = self.xpath('./w:r/w:footnoteReference/@w:id') - if len(_id) > 1 or len(_id) == 0 : + if len(_id) == 0 : return None else: - return int(_id[0]) + return _id @style.setter diff --git a/build/lib/docx/text/paragraph.py b/build/lib/docx/text/paragraph.py index 4a04c0687..eb8e3f66f 100644 --- a/build/lib/docx/text/paragraph.py +++ b/build/lib/docx/text/paragraph.py @@ -157,6 +157,13 @@ def text(self): text += run.text return text + @property + def footnotes(self): + if self._p.footnote_ids is not None : + return True + else : + return False + @text.setter def text(self, text): self.clear() diff --git a/build/lib/docx/text/run.py b/build/lib/docx/text/run.py index 97d6da7db..8f2ea787a 100644 --- a/build/lib/docx/text/run.py +++ b/build/lib/docx/text/run.py @@ -180,6 +180,17 @@ def underline(self): @underline.setter def underline(self, value): self.font.underline = value + + @property + def footnote(self): + _id = self._r.footnote_id + + if _id is not None: + footnotes_part = self._parent._parent.part._footnotes_part.element + footnote = footnotes_part.get_footnote_by_id(_id) + return footnote.paragraph.text + else: + return None class _Text(object): diff --git a/docx/__init__.py b/docx/__init__.py index 59bc452ae..5a5a4ff42 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.8.7' +__version__ = '0.1.2' # register custom Part classes with opc package reader diff --git a/docx/document.py b/docx/document.py index 3747f0e36..f3b2e689f 100644 --- a/docx/document.py +++ b/docx/document.py @@ -116,13 +116,13 @@ def comments_part(self): """ return self.part.comments_part - @property - def footnotes_part(self): - """ - A |Footnotes| object providing read/write access to the core - properties of this document. - """ - return self.part._footnotes_part + # @property + # def footnotes_part(self): + # """ + # A |Footnotes| object providing read/write access to the core + # properties of this document. + # """ + # return self.part._footnotes_part diff --git a/setup.py b/setup.py index bb248dafc..f4979840b 100644 --- a/setup.py +++ b/setup.py @@ -25,13 +25,13 @@ def text_of(relpath): ).group(1) -NAME = 'python-docx' +NAME = 'bayoo-docx' VERSION = version DESCRIPTION = 'Create and update Microsoft Word .docx files.' KEYWORDS = 'docx office openxml word' -AUTHOR = 'Steve Canny' -AUTHOR_EMAIL = 'python-docx@googlegroups.com' -URL = 'https://github.com/python-openxml/python-docx' +AUTHOR = 'Obay Daba' +AUTHOR_EMAIL = 'ObayDaba96@googlegroups.com' +URL = 'https://github.com/BayooG/bayooo-docx' LICENSE = text_of('LICENSE') PACKAGES = find_packages(exclude=['tests', 'tests.*']) PACKAGE_DATA = {'docx': ['templates/*']} From 0547bbdb1aeb85f279406bdd34a0409c93d1c6b9 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 8 Mar 2019 00:29:43 +0200 Subject: [PATCH 13/70] V 0.1.3 --- HISTORY.rst | 4 ++-- README.rst | 7 +------ build/lib/docx/__init__.py | 2 +- docx/__init__.py | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index d77fd583c..beea4e998 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,10 +1,10 @@ .. :changelog: -Release History BayooG/bayooo-docx forked from (python-openxmm/python-docx) +Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) --------------- -0.1.2 (2019-03-07) +0.1.3 (2019-03-08) ++++++++++++++++++ - Add low-level implementation for comments part diff --git a/README.rst b/README.rst index 82d1f0bd7..5d55db2b9 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,5 @@ -.. image:: https://travis-ci.org/python-openxml/python-docx.svg?branch=master - :target: https://travis-ci.org/python-openxml/python-docx -*python-docx* is a Python library for creating and updating Microsoft Word +*bayoo-docx* is a Python library for creating and updating Microsoft Word (.docx) files. -More information is available in the `python-docx documentation`_. -.. _`python-docx documentation`: - https://python-docx.readthedocs.org/en/latest/ diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py index 5a5a4ff42..a5ab073ca 100644 --- a/build/lib/docx/__init__.py +++ b/build/lib/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.2' +__version__ = '0.1.3' # register custom Part classes with opc package reader diff --git a/docx/__init__.py b/docx/__init__.py index 5a5a4ff42..a5ab073ca 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.2' +__version__ = '0.1.3' # register custom Part classes with opc package reader From db913ae4e6be1a85142630881852ca92804392ea Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Wed, 20 Mar 2019 14:07:10 +0200 Subject: [PATCH 14/70] tbl : oxml elements inside tbl --- build/lib/docx/__init__.py | 2 +- build/lib/docx/oxml/__init__.py | 4 +++- build/lib/docx/oxml/table.py | 10 ++++++++++ docx/__init__.py | 2 +- docx/oxml/__init__.py | 4 +++- docx/oxml/table.py | 10 ++++++++++ 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py index a5ab073ca..3c8c36ed0 100644 --- a/build/lib/docx/__init__.py +++ b/build/lib/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.3' +__version__ = '0.1.4' # register custom Part classes with opc package reader diff --git a/build/lib/docx/oxml/__init__.py b/build/lib/docx/oxml/__init__.py index 0560633ab..8851bc7b0 100644 --- a/build/lib/docx/oxml/__init__.py +++ b/build/lib/docx/oxml/__init__.py @@ -129,7 +129,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): from .table import ( CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, - CT_TblPr, CT_TblWidth, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge + CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge ) register_element_cls('w:bidiVisual', CT_OnOff) register_element_cls('w:gridCol', CT_TblGridCol) @@ -138,6 +138,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:tblGrid', CT_TblGrid) register_element_cls('w:tblLayout', CT_TblLayoutType) register_element_cls('w:tblPr', CT_TblPr) +register_element_cls('w:tblW', CT_TblWidth) +register_element_cls('w:tblCellMar', CT_TblMar) register_element_cls('w:tblStyle', CT_String) register_element_cls('w:tc', CT_Tc) register_element_cls('w:tcPr', CT_TcPr) diff --git a/build/lib/docx/oxml/table.py b/build/lib/docx/oxml/table.py index 95f9c6243..6f69db835 100644 --- a/build/lib/docx/oxml/table.py +++ b/build/lib/docx/oxml/table.py @@ -280,6 +280,8 @@ class CT_TblPr(BaseOxmlElement): ) tblStyle = ZeroOrOne('w:tblStyle', successors=_tag_seq[1:]) bidiVisual = ZeroOrOne('w:bidiVisual', successors=_tag_seq[4:]) + tblW =ZeroOrOne ('w:tblW', successors=('w:tblPr',)) + tblCellMar = ZeroOrOne('w:tblCellMar', successors=('w:tblPr',)) jc = ZeroOrOne('w:jc', successors=_tag_seq[8:]) tblLayout = ZeroOrOne('w:tblLayout', successors=_tag_seq[13:]) del _tag_seq @@ -893,3 +895,11 @@ class CT_VMerge(BaseOxmlElement): ```` element, specifying vertical merging behavior of a cell. """ val = OptionalAttribute('w:val', ST_Merge, default=ST_Merge.CONTINUE) + + +class CT_TblMar(BaseOxmlElement): + """ + ```` element + """ + left = ZeroOrOne('w:left', successors=('w:tblCellMar',)) + right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) \ No newline at end of file diff --git a/docx/__init__.py b/docx/__init__.py index a5ab073ca..3c8c36ed0 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.3' +__version__ = '0.1.4' # register custom Part classes with opc package reader diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 0560633ab..8851bc7b0 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -129,7 +129,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): from .table import ( CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, - CT_TblPr, CT_TblWidth, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge + CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge ) register_element_cls('w:bidiVisual', CT_OnOff) register_element_cls('w:gridCol', CT_TblGridCol) @@ -138,6 +138,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:tblGrid', CT_TblGrid) register_element_cls('w:tblLayout', CT_TblLayoutType) register_element_cls('w:tblPr', CT_TblPr) +register_element_cls('w:tblW', CT_TblWidth) +register_element_cls('w:tblCellMar', CT_TblMar) register_element_cls('w:tblStyle', CT_String) register_element_cls('w:tc', CT_Tc) register_element_cls('w:tcPr', CT_TcPr) diff --git a/docx/oxml/table.py b/docx/oxml/table.py index 95f9c6243..6f69db835 100644 --- a/docx/oxml/table.py +++ b/docx/oxml/table.py @@ -280,6 +280,8 @@ class CT_TblPr(BaseOxmlElement): ) tblStyle = ZeroOrOne('w:tblStyle', successors=_tag_seq[1:]) bidiVisual = ZeroOrOne('w:bidiVisual', successors=_tag_seq[4:]) + tblW =ZeroOrOne ('w:tblW', successors=('w:tblPr',)) + tblCellMar = ZeroOrOne('w:tblCellMar', successors=('w:tblPr',)) jc = ZeroOrOne('w:jc', successors=_tag_seq[8:]) tblLayout = ZeroOrOne('w:tblLayout', successors=_tag_seq[13:]) del _tag_seq @@ -893,3 +895,11 @@ class CT_VMerge(BaseOxmlElement): ```` element, specifying vertical merging behavior of a cell. """ val = OptionalAttribute('w:val', ST_Merge, default=ST_Merge.CONTINUE) + + +class CT_TblMar(BaseOxmlElement): + """ + ```` element + """ + left = ZeroOrOne('w:left', successors=('w:tblCellMar',)) + right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) \ No newline at end of file From e6abc80525c1100ed1bc1ba86a8ab907914c8137 Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Wed, 27 Mar 2019 18:22:22 +0200 Subject: [PATCH 15/70] added emf support! --- docx/image/__init__.py | 3 +- docx/image/constants.py | 2 +- docx/image/emf.py | 70 +++++++++++++++++++++++++++++++++++++++++ docx/image/image.py | 2 +- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 docx/image/emf.py diff --git a/docx/image/__init__.py b/docx/image/__init__.py index 8ab3ada68..30b8d45d5 100644 --- a/docx/image/__init__.py +++ b/docx/image/__init__.py @@ -14,7 +14,7 @@ from docx.image.jpeg import Exif, Jfif from docx.image.png import Png from docx.image.tiff import Tiff - +from docx.image.emf import Emf SIGNATURES = ( # class, offset, signature_bytes @@ -26,4 +26,5 @@ (Tiff, 0, b'MM\x00*'), # big-endian (Motorola) TIFF (Tiff, 0, b'II*\x00'), # little-endian (Intel) TIFF (Bmp, 0, b'BM'), + (Emf, 40, b' EMF') ) diff --git a/docx/image/constants.py b/docx/image/constants.py index 90b469705..97d40e314 100644 --- a/docx/image/constants.py +++ b/docx/image/constants.py @@ -102,7 +102,7 @@ class MIME_TYPE(object): JPEG = 'image/jpeg' PNG = 'image/png' TIFF = 'image/tiff' - + EMF = 'image/emf' class PNG_CHUNK_TYPE(object): """ diff --git a/docx/image/emf.py b/docx/image/emf.py new file mode 100644 index 000000000..e2701c04f --- /dev/null +++ b/docx/image/emf.py @@ -0,0 +1,70 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from .constants import MIME_TYPE +from .exceptions import InvalidImageStreamError +from .helpers import BIG_ENDIAN, StreamReader +from .image import BaseImageHeader +import struct + +class Emf(BaseImageHeader): + """ + Image header parser for PNG images + """ + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/png` for + PNG images. + """ + return MIME_TYPE.EMF + + @property + def default_ext(self): + """ + Default filename extension, always 'png' for PNG images. + """ + return 'emf' + + @classmethod + def from_stream(cls, stream,filename=None): + """ + Return a |Emf| instance having header properties parsed from image in + *stream*. + """ + + """ + @0 DWORD iType; // fixed + @4 DWORD nSize; // var + @8 RECTL rclBounds; + @24 RECTL rclFrame; // .01 millimeter units L T R B + @40 DWORD dSignature; // ENHMETA_SIGNATURE = 0x464D4520 + DWORD nVersion; + DWORD nBytes; + DWORD nRecords; + WORD nHandles; + WORD sReserved; + DWORD nDescription; + DWORD offDescription; + DWORD nPalEntries; + SIZEL szlDevice; + SIZEL szlMillimeters; + """ + stream.seek(0) + x = stream.read(40) + stream.seek(0) + iType,nSize = struct.unpack("ii",x[0:8]) + rclBounds = struct.unpack("iiii",x[8:24]) + rclFrame = struct.unpack("iiii",x[24:40]) + + dpi = 300 + horz_dpi = dpi + vert_dpi = dpi + mmwidth = (rclFrame[2]-rclFrame[0])/100.0 + mmheight = (rclFrame[3]-rclFrame[1])/100.0 + px_width = int(mmwidth*dpi*0.03937008) + px_height = int(mmheight*dpi*0.03937008) + + #1 dot/inch = 0.03937008 pixel/millimeter + return cls(px_width,px_height,horz_dpi,vert_dpi) \ No newline at end of file diff --git a/docx/image/image.py b/docx/image/image.py index ba2158e72..3df31672f 100644 --- a/docx/image/image.py +++ b/docx/image/image.py @@ -188,7 +188,7 @@ def _ImageHeaderFactory(stream): def read_32(stream): stream.seek(0) - return stream.read(32) + return stream.read(64) header = read_32(stream) for cls, offset, signature_bytes in SIGNATURES: From 2e519dd41726199e48b147aff17807e3305d63df Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Thu, 28 Mar 2019 12:28:52 +0200 Subject: [PATCH 16/70] version update! --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 3c8c36ed0..afd8aa5da 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.4' +__version__ = '0.1.5' # register custom Part classes with opc package reader From b04c0a1d8d4a2992cacc7cb44b72f4f72841d83f Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 25 Apr 2019 18:28:25 +0300 Subject: [PATCH 17/70] CT_TblBoarders added --- docx/__init__.py | 2 +- docx/oxml/__init__.py | 3 ++- docx/oxml/table.py | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docx/__init__.py b/docx/__init__.py index afd8aa5da..91a9c3b42 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.5' +__version__ = '0.1.6' # register custom Part classes with opc package reader diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 8851bc7b0..0d932cd2d 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -129,7 +129,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): from .table import ( CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, - CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge + CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge, CT_TblBoarders ) register_element_cls('w:bidiVisual', CT_OnOff) register_element_cls('w:gridCol', CT_TblGridCol) @@ -149,6 +149,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:trPr', CT_TrPr) register_element_cls('w:vAlign', CT_VerticalJc) register_element_cls('w:vMerge', CT_VMerge) +register_element_cls('w:tblBorders', CT_TblBoarders) from .text.font import ( CT_Color, CT_Fonts, CT_Highlight, CT_HpsMeasure, CT_RPr, CT_Underline, diff --git a/docx/oxml/table.py b/docx/oxml/table.py index 6f69db835..ccce34c42 100644 --- a/docx/oxml/table.py +++ b/docx/oxml/table.py @@ -265,6 +265,8 @@ class CT_TblLayoutType(BaseOxmlElement): """ type = OptionalAttribute('w:type', ST_TblLayoutType) +class CT_TblBoarders(BaseOxmlElement): + pass class CT_TblPr(BaseOxmlElement): """ @@ -284,6 +286,7 @@ class CT_TblPr(BaseOxmlElement): tblCellMar = ZeroOrOne('w:tblCellMar', successors=('w:tblPr',)) jc = ZeroOrOne('w:jc', successors=_tag_seq[8:]) tblLayout = ZeroOrOne('w:tblLayout', successors=_tag_seq[13:]) + tblBorders = ZeroOrOne('w:tblBorders', successors=('w:tblPr',)) del _tag_seq @property From 5685a3f2326311a45fdbe3f5270079c47e11d16f Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sun, 28 Apr 2019 13:42:04 +0300 Subject: [PATCH 18/70] add tcBorders --- build/lib/docx/__init__.py | 2 +- build/lib/docx/image/emf.py | 70 +++++++++++++++++++++++++++++++++ build/lib/docx/image/image.py | 2 +- build/lib/docx/oxml/__init__.py | 6 ++- build/lib/docx/oxml/table.py | 53 ++++++++++++++++++++++++- docx/__init__.py | 2 +- docx/oxml/__init__.py | 5 ++- docx/oxml/table.py | 50 ++++++++++++++++++++++- 8 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 build/lib/docx/image/emf.py diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py index 3c8c36ed0..a876023a7 100644 --- a/build/lib/docx/__init__.py +++ b/build/lib/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.4' +__version__ = '0.1.7' # register custom Part classes with opc package reader diff --git a/build/lib/docx/image/emf.py b/build/lib/docx/image/emf.py new file mode 100644 index 000000000..e2701c04f --- /dev/null +++ b/build/lib/docx/image/emf.py @@ -0,0 +1,70 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from .constants import MIME_TYPE +from .exceptions import InvalidImageStreamError +from .helpers import BIG_ENDIAN, StreamReader +from .image import BaseImageHeader +import struct + +class Emf(BaseImageHeader): + """ + Image header parser for PNG images + """ + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/png` for + PNG images. + """ + return MIME_TYPE.EMF + + @property + def default_ext(self): + """ + Default filename extension, always 'png' for PNG images. + """ + return 'emf' + + @classmethod + def from_stream(cls, stream,filename=None): + """ + Return a |Emf| instance having header properties parsed from image in + *stream*. + """ + + """ + @0 DWORD iType; // fixed + @4 DWORD nSize; // var + @8 RECTL rclBounds; + @24 RECTL rclFrame; // .01 millimeter units L T R B + @40 DWORD dSignature; // ENHMETA_SIGNATURE = 0x464D4520 + DWORD nVersion; + DWORD nBytes; + DWORD nRecords; + WORD nHandles; + WORD sReserved; + DWORD nDescription; + DWORD offDescription; + DWORD nPalEntries; + SIZEL szlDevice; + SIZEL szlMillimeters; + """ + stream.seek(0) + x = stream.read(40) + stream.seek(0) + iType,nSize = struct.unpack("ii",x[0:8]) + rclBounds = struct.unpack("iiii",x[8:24]) + rclFrame = struct.unpack("iiii",x[24:40]) + + dpi = 300 + horz_dpi = dpi + vert_dpi = dpi + mmwidth = (rclFrame[2]-rclFrame[0])/100.0 + mmheight = (rclFrame[3]-rclFrame[1])/100.0 + px_width = int(mmwidth*dpi*0.03937008) + px_height = int(mmheight*dpi*0.03937008) + + #1 dot/inch = 0.03937008 pixel/millimeter + return cls(px_width,px_height,horz_dpi,vert_dpi) \ No newline at end of file diff --git a/build/lib/docx/image/image.py b/build/lib/docx/image/image.py index ba2158e72..3df31672f 100644 --- a/build/lib/docx/image/image.py +++ b/build/lib/docx/image/image.py @@ -188,7 +188,7 @@ def _ImageHeaderFactory(stream): def read_32(stream): stream.seek(0) - return stream.read(32) + return stream.read(64) header = read_32(stream) for cls, offset, signature_bytes in SIGNATURES: diff --git a/build/lib/docx/oxml/__init__.py b/build/lib/docx/oxml/__init__.py index 8851bc7b0..83ca3ce40 100644 --- a/build/lib/docx/oxml/__init__.py +++ b/build/lib/docx/oxml/__init__.py @@ -129,7 +129,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): from .table import ( CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, - CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge + CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, + CT_VMerge, CT_TblBoarders, CT_Bottom, CT_TcBorders ) register_element_cls('w:bidiVisual', CT_OnOff) register_element_cls('w:gridCol', CT_TblGridCol) @@ -149,6 +150,9 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:trPr', CT_TrPr) register_element_cls('w:vAlign', CT_VerticalJc) register_element_cls('w:vMerge', CT_VMerge) +register_element_cls('w:tblBorders', CT_TblBoarders) +register_element_cls('w:tcBorders', CT_TcBorders) +register_element_cls('w:bottom', CT_Bottom) from .text.font import ( CT_Color, CT_Fonts, CT_Highlight, CT_HpsMeasure, CT_RPr, CT_Underline, diff --git a/build/lib/docx/oxml/table.py b/build/lib/docx/oxml/table.py index 6f69db835..403aa88a5 100644 --- a/build/lib/docx/oxml/table.py +++ b/build/lib/docx/oxml/table.py @@ -7,12 +7,13 @@ ) from . import parse_xml +from . import OxmlElement from ..enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE from ..exceptions import InvalidSpanError from .ns import nsdecls, qn from ..shared import Emu, Twips from .simpletypes import ( - ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt + ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt, ST_String ) from .xmlchemy import ( BaseOxmlElement, OneAndOnlyOne, OneOrMore, OptionalAttribute, @@ -265,6 +266,8 @@ class CT_TblLayoutType(BaseOxmlElement): """ type = OptionalAttribute('w:type', ST_TblLayoutType) +class CT_TblBoarders(BaseOxmlElement): + pass class CT_TblPr(BaseOxmlElement): """ @@ -284,6 +287,7 @@ class CT_TblPr(BaseOxmlElement): tblCellMar = ZeroOrOne('w:tblCellMar', successors=('w:tblPr',)) jc = ZeroOrOne('w:jc', successors=_tag_seq[8:]) tblLayout = ZeroOrOne('w:tblLayout', successors=_tag_seq[13:]) + tblBorders = ZeroOrOne('w:tblBorders', successors=('w:tblPr',)) del _tag_seq @property @@ -750,6 +754,29 @@ def _tr_idx(self): """ return self._tbl.tr_lst.index(self._tr) +class CT_TcBorders(BaseOxmlElement): + """ + element + """ + top = ZeroOrOne('w:top') + start = ZeroOrOne('w:start') + bottom = ZeroOrOne('w:bottom',successors=('w:tblPr',) ) + end = ZeroOrOne('w:end') + + + def new(cls): + """ + Return a new ```` element + """ + return parse_xml( + '\n' + '' % nsdecls('w') + ) + + def add_bottom_border(self, val, sz): + bottom = CT_Bottom.new ( val, sz) + return self._insert_bottom(bottom) + class CT_TcPr(BaseOxmlElement): """ @@ -763,8 +790,10 @@ class CT_TcPr(BaseOxmlElement): ) tcW = ZeroOrOne('w:tcW', successors=_tag_seq[2:]) gridSpan = ZeroOrOne('w:gridSpan', successors=_tag_seq[3:]) + tcBorders = ZeroOrOne('w:tcBorders', successors = ('w:tcPr',)) vMerge = ZeroOrOne('w:vMerge', successors=_tag_seq[5:]) vAlign = ZeroOrOne('w:vAlign', successors=_tag_seq[12:]) + del _tag_seq @property @@ -902,4 +931,24 @@ class CT_TblMar(BaseOxmlElement): ```` element """ left = ZeroOrOne('w:left', successors=('w:tblCellMar',)) - right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) \ No newline at end of file + right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) + + +class CT_Bottom(BaseOxmlElement): + """ + element + """ + val= OptionalAttribute('w:val', ST_String) + sz= OptionalAttribute('w:sz', ST_String) + space = OptionalAttribute('w:space', ST_String) + color = OptionalAttribute('w:color', ST_String) + + @classmethod + def new(cls, val, sz): + bottom = OxmlElement('w:bottom') + bottom.val = val + bottom.sz = sz + bottom.space = "0" + bottom.color = "auto" + + return bottom diff --git a/docx/__init__.py b/docx/__init__.py index 91a9c3b42..a876023a7 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.6' +__version__ = '0.1.7' # register custom Part classes with opc package reader diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 0d932cd2d..83ca3ce40 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -129,7 +129,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): from .table import ( CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, - CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, CT_VMerge, CT_TblBoarders + CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, + CT_VMerge, CT_TblBoarders, CT_Bottom, CT_TcBorders ) register_element_cls('w:bidiVisual', CT_OnOff) register_element_cls('w:gridCol', CT_TblGridCol) @@ -150,6 +151,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:vAlign', CT_VerticalJc) register_element_cls('w:vMerge', CT_VMerge) register_element_cls('w:tblBorders', CT_TblBoarders) +register_element_cls('w:tcBorders', CT_TcBorders) +register_element_cls('w:bottom', CT_Bottom) from .text.font import ( CT_Color, CT_Fonts, CT_Highlight, CT_HpsMeasure, CT_RPr, CT_Underline, diff --git a/docx/oxml/table.py b/docx/oxml/table.py index ccce34c42..403aa88a5 100644 --- a/docx/oxml/table.py +++ b/docx/oxml/table.py @@ -7,12 +7,13 @@ ) from . import parse_xml +from . import OxmlElement from ..enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE from ..exceptions import InvalidSpanError from .ns import nsdecls, qn from ..shared import Emu, Twips from .simpletypes import ( - ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt + ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt, ST_String ) from .xmlchemy import ( BaseOxmlElement, OneAndOnlyOne, OneOrMore, OptionalAttribute, @@ -753,6 +754,29 @@ def _tr_idx(self): """ return self._tbl.tr_lst.index(self._tr) +class CT_TcBorders(BaseOxmlElement): + """ + element + """ + top = ZeroOrOne('w:top') + start = ZeroOrOne('w:start') + bottom = ZeroOrOne('w:bottom',successors=('w:tblPr',) ) + end = ZeroOrOne('w:end') + + + def new(cls): + """ + Return a new ```` element + """ + return parse_xml( + '\n' + '' % nsdecls('w') + ) + + def add_bottom_border(self, val, sz): + bottom = CT_Bottom.new ( val, sz) + return self._insert_bottom(bottom) + class CT_TcPr(BaseOxmlElement): """ @@ -766,8 +790,10 @@ class CT_TcPr(BaseOxmlElement): ) tcW = ZeroOrOne('w:tcW', successors=_tag_seq[2:]) gridSpan = ZeroOrOne('w:gridSpan', successors=_tag_seq[3:]) + tcBorders = ZeroOrOne('w:tcBorders', successors = ('w:tcPr',)) vMerge = ZeroOrOne('w:vMerge', successors=_tag_seq[5:]) vAlign = ZeroOrOne('w:vAlign', successors=_tag_seq[12:]) + del _tag_seq @property @@ -905,4 +931,24 @@ class CT_TblMar(BaseOxmlElement): ```` element """ left = ZeroOrOne('w:left', successors=('w:tblCellMar',)) - right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) \ No newline at end of file + right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) + + +class CT_Bottom(BaseOxmlElement): + """ + element + """ + val= OptionalAttribute('w:val', ST_String) + sz= OptionalAttribute('w:sz', ST_String) + space = OptionalAttribute('w:space', ST_String) + color = OptionalAttribute('w:color', ST_String) + + @classmethod + def new(cls, val, sz): + bottom = OxmlElement('w:bottom') + bottom.val = val + bottom.sz = sz + bottom.space = "0" + bottom.color = "auto" + + return bottom From c203563ef27e0b1b631e72250bb9439d91ef4a9a Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Tue, 30 Apr 2019 16:30:09 +0300 Subject: [PATCH 19/70] oxml: convert CT_AbstractNum to xmlchemy --- docx/oxml/__init__.py | 3 ++- docx/oxml/numbering.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 83ca3ce40..349752868 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -75,12 +75,13 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:document', CT_Document) from .numbering import ( - CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr + CT_Num, CT_AbstractNum, CT_Numbering, CT_NumLvl, CT_NumPr ) register_element_cls('w:abstractNumId', CT_DecimalNumber) register_element_cls('w:ilvl', CT_DecimalNumber) register_element_cls('w:lvlOverride', CT_NumLvl) register_element_cls('w:num', CT_Num) +register_element_cls('w:abstractNum', CT_AbstractNum) register_element_cls('w:numId', CT_DecimalNumber) register_element_cls('w:numPr', CT_NumPr) register_element_cls('w:numbering', CT_Numbering) diff --git a/docx/oxml/numbering.py b/docx/oxml/numbering.py index aeedfa9a0..6f26ee150 100644 --- a/docx/oxml/numbering.py +++ b/docx/oxml/numbering.py @@ -45,6 +45,13 @@ def new(cls, num_id, abstractNum_id): return num +class CT_AbstractNum(BaseOxmlElement): + """ + ```` element, which represents an abstract numbering definition that defines most of the formatting details. + """ + abstractNumId = RequiredAttribute('w:abstractNumId', ST_DecimalNumber) + + class CT_NumLvl(BaseOxmlElement): """ ```` element, which identifies a level in a list @@ -94,6 +101,7 @@ class CT_Numbering(BaseOxmlElement): ```` element, the root element of a numbering part, i.e. numbering.xml """ + abstractNum = ZeroOrMore('w:abstractNum', successors=('w:num',)) num = ZeroOrMore('w:num', successors=('w:numIdMacAtCleanup',)) def add_num(self, abstractNum_id): From cb4038234ce3cd85f41344e76017b67be8b77337 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Tue, 30 Apr 2019 16:38:21 +0300 Subject: [PATCH 20/70] release 0.1.8 --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index a876023a7..37012d2a9 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.7' +__version__ = '0.1.8' # register custom Part classes with opc package reader From ea2f511a8dcbdc25599751ae35b13b55c3c1fb36 Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Mon, 6 May 2019 14:10:21 +0300 Subject: [PATCH 21/70] Version update! --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 37012d2a9..71337237a 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.8' +__version__ = '0.1.9' # register custom Part classes with opc package reader From 8e93e43f126a967fbf9ef10c93a2cfef88e2e7ab Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Tue, 7 May 2019 13:18:35 +0300 Subject: [PATCH 22/70] Version Update! --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 71337237a..013ddcf0b 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.1.9' +__version__ = '0.2.0' # register custom Part classes with opc package reader From 84e7f1ee183b6e2bc2b1f058dff56078483defe2 Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Tue, 7 May 2019 14:03:12 +0300 Subject: [PATCH 23/70] ignore build folder --- .gitignore | 1 + build/lib/docx/__init__.py | 41 - build/lib/docx/api.py | 37 - build/lib/docx/blkcntnr.py | 74 -- build/lib/docx/compat.py | 43 - build/lib/docx/dml/__init__.py | 0 build/lib/docx/dml/color.py | 116 --- build/lib/docx/document.py | 233 ----- build/lib/docx/enum/__init__.py | 18 - build/lib/docx/enum/base.py | 363 ------- build/lib/docx/enum/dml.py | 124 --- build/lib/docx/enum/section.py | 76 -- build/lib/docx/enum/shape.py | 21 - build/lib/docx/enum/style.py | 466 --------- build/lib/docx/enum/table.py | 146 --- build/lib/docx/enum/text.py | 351 ------- build/lib/docx/exceptions.py | 27 - build/lib/docx/image/__init__.py | 29 - build/lib/docx/image/bmp.py | 56 - build/lib/docx/image/constants.py | 169 ---- build/lib/docx/image/emf.py | 70 -- build/lib/docx/image/exceptions.py | 23 - build/lib/docx/image/gif.py | 47 - build/lib/docx/image/helpers.py | 97 -- build/lib/docx/image/image.py | 263 ----- build/lib/docx/image/jpeg.py | 498 --------- build/lib/docx/image/png.py | 303 ------ build/lib/docx/image/tiff.py | 345 ------- build/lib/docx/opc/__init__.py | 0 build/lib/docx/opc/compat.py | 50 - build/lib/docx/opc/constants.py | 658 ------------ build/lib/docx/opc/coreprops.py | 139 --- build/lib/docx/opc/exceptions.py | 19 - build/lib/docx/opc/oxml.py | 292 ------ build/lib/docx/opc/package.py | 249 ----- build/lib/docx/opc/packuri.py | 117 --- build/lib/docx/opc/part.py | 241 ----- build/lib/docx/opc/parts/__init__.py | 0 build/lib/docx/opc/parts/coreprops.py | 54 - build/lib/docx/opc/phys_pkg.py | 155 --- build/lib/docx/opc/pkgreader.py | 298 ------ build/lib/docx/opc/pkgwriter.py | 125 --- build/lib/docx/opc/rel.py | 170 ---- build/lib/docx/opc/shared.py | 47 - build/lib/docx/opc/spec.py | 29 - build/lib/docx/oxml/__init__.py | 228 ----- build/lib/docx/oxml/comments.py | 129 --- build/lib/docx/oxml/coreprops.py | 317 ------ build/lib/docx/oxml/document.py | 59 -- build/lib/docx/oxml/exceptions.py | 16 - build/lib/docx/oxml/footnotes.py | 89 -- build/lib/docx/oxml/ns.py | 114 --- build/lib/docx/oxml/numbering.py | 131 --- build/lib/docx/oxml/section.py | 264 ----- build/lib/docx/oxml/shape.py | 284 ------ build/lib/docx/oxml/shared.py | 55 - build/lib/docx/oxml/simpletypes.py | 409 -------- build/lib/docx/oxml/styles.py | 351 ------- build/lib/docx/oxml/table.py | 954 ------------------ build/lib/docx/oxml/text/__init__.py | 0 build/lib/docx/oxml/text/font.py | 320 ------ build/lib/docx/oxml/text/paragraph.py | 135 --- build/lib/docx/oxml/text/parfmt.py | 348 ------- build/lib/docx/oxml/text/run.py | 224 ---- build/lib/docx/oxml/xmlchemy.py | 761 -------------- build/lib/docx/package.py | 115 --- build/lib/docx/parts/__init__.py | 0 build/lib/docx/parts/comments.py | 26 - build/lib/docx/parts/document.py | 206 ---- build/lib/docx/parts/footnotes.py | 26 - build/lib/docx/parts/image.py | 89 -- build/lib/docx/parts/numbering.py | 47 - build/lib/docx/parts/settings.py | 54 - build/lib/docx/parts/styles.py | 55 - build/lib/docx/section.py | 185 ---- build/lib/docx/settings.py | 20 - build/lib/docx/shape.py | 103 -- build/lib/docx/shared.py | 250 ----- build/lib/docx/styles/__init__.py | 50 - build/lib/docx/styles/latent.py | 224 ---- build/lib/docx/styles/style.py | 265 ----- build/lib/docx/styles/styles.py | 157 --- build/lib/docx/table.py | 469 --------- build/lib/docx/templates/default-comments.xml | 2 - .../lib/docx/templates/default-footnotes.xml | 6 - build/lib/docx/templates/default-settings.xml | 26 - build/lib/docx/templates/default-src.docx | Bin 77650 -> 0 bytes build/lib/docx/templates/default-styles.xml | 190 ---- build/lib/docx/templates/default.docx | Bin 38003 -> 0 bytes build/lib/docx/text/__init__.py | 0 build/lib/docx/text/font.py | 411 -------- build/lib/docx/text/paragraph.py | 178 ---- build/lib/docx/text/parfmt.py | 303 ------ build/lib/docx/text/run.py | 202 ---- build/lib/docx/text/tabstops.py | 143 --- 95 files changed, 1 insertion(+), 15669 deletions(-) delete mode 100644 build/lib/docx/__init__.py delete mode 100644 build/lib/docx/api.py delete mode 100644 build/lib/docx/blkcntnr.py delete mode 100644 build/lib/docx/compat.py delete mode 100644 build/lib/docx/dml/__init__.py delete mode 100644 build/lib/docx/dml/color.py delete mode 100644 build/lib/docx/document.py delete mode 100644 build/lib/docx/enum/__init__.py delete mode 100644 build/lib/docx/enum/base.py delete mode 100644 build/lib/docx/enum/dml.py delete mode 100644 build/lib/docx/enum/section.py delete mode 100644 build/lib/docx/enum/shape.py delete mode 100644 build/lib/docx/enum/style.py delete mode 100644 build/lib/docx/enum/table.py delete mode 100644 build/lib/docx/enum/text.py delete mode 100644 build/lib/docx/exceptions.py delete mode 100644 build/lib/docx/image/__init__.py delete mode 100644 build/lib/docx/image/bmp.py delete mode 100644 build/lib/docx/image/constants.py delete mode 100644 build/lib/docx/image/emf.py delete mode 100644 build/lib/docx/image/exceptions.py delete mode 100644 build/lib/docx/image/gif.py delete mode 100644 build/lib/docx/image/helpers.py delete mode 100644 build/lib/docx/image/image.py delete mode 100644 build/lib/docx/image/jpeg.py delete mode 100644 build/lib/docx/image/png.py delete mode 100644 build/lib/docx/image/tiff.py delete mode 100644 build/lib/docx/opc/__init__.py delete mode 100644 build/lib/docx/opc/compat.py delete mode 100644 build/lib/docx/opc/constants.py delete mode 100644 build/lib/docx/opc/coreprops.py delete mode 100644 build/lib/docx/opc/exceptions.py delete mode 100644 build/lib/docx/opc/oxml.py delete mode 100644 build/lib/docx/opc/package.py delete mode 100644 build/lib/docx/opc/packuri.py delete mode 100644 build/lib/docx/opc/part.py delete mode 100644 build/lib/docx/opc/parts/__init__.py delete mode 100644 build/lib/docx/opc/parts/coreprops.py delete mode 100644 build/lib/docx/opc/phys_pkg.py delete mode 100644 build/lib/docx/opc/pkgreader.py delete mode 100644 build/lib/docx/opc/pkgwriter.py delete mode 100644 build/lib/docx/opc/rel.py delete mode 100644 build/lib/docx/opc/shared.py delete mode 100644 build/lib/docx/opc/spec.py delete mode 100644 build/lib/docx/oxml/__init__.py delete mode 100644 build/lib/docx/oxml/comments.py delete mode 100644 build/lib/docx/oxml/coreprops.py delete mode 100644 build/lib/docx/oxml/document.py delete mode 100644 build/lib/docx/oxml/exceptions.py delete mode 100644 build/lib/docx/oxml/footnotes.py delete mode 100644 build/lib/docx/oxml/ns.py delete mode 100644 build/lib/docx/oxml/numbering.py delete mode 100644 build/lib/docx/oxml/section.py delete mode 100644 build/lib/docx/oxml/shape.py delete mode 100644 build/lib/docx/oxml/shared.py delete mode 100644 build/lib/docx/oxml/simpletypes.py delete mode 100644 build/lib/docx/oxml/styles.py delete mode 100644 build/lib/docx/oxml/table.py delete mode 100644 build/lib/docx/oxml/text/__init__.py delete mode 100644 build/lib/docx/oxml/text/font.py delete mode 100644 build/lib/docx/oxml/text/paragraph.py delete mode 100644 build/lib/docx/oxml/text/parfmt.py delete mode 100644 build/lib/docx/oxml/text/run.py delete mode 100644 build/lib/docx/oxml/xmlchemy.py delete mode 100644 build/lib/docx/package.py delete mode 100644 build/lib/docx/parts/__init__.py delete mode 100644 build/lib/docx/parts/comments.py delete mode 100644 build/lib/docx/parts/document.py delete mode 100644 build/lib/docx/parts/footnotes.py delete mode 100644 build/lib/docx/parts/image.py delete mode 100644 build/lib/docx/parts/numbering.py delete mode 100644 build/lib/docx/parts/settings.py delete mode 100644 build/lib/docx/parts/styles.py delete mode 100644 build/lib/docx/section.py delete mode 100644 build/lib/docx/settings.py delete mode 100644 build/lib/docx/shape.py delete mode 100644 build/lib/docx/shared.py delete mode 100644 build/lib/docx/styles/__init__.py delete mode 100644 build/lib/docx/styles/latent.py delete mode 100644 build/lib/docx/styles/style.py delete mode 100644 build/lib/docx/styles/styles.py delete mode 100644 build/lib/docx/table.py delete mode 100644 build/lib/docx/templates/default-comments.xml delete mode 100644 build/lib/docx/templates/default-footnotes.xml delete mode 100644 build/lib/docx/templates/default-settings.xml delete mode 100644 build/lib/docx/templates/default-src.docx delete mode 100644 build/lib/docx/templates/default-styles.xml delete mode 100644 build/lib/docx/templates/default.docx delete mode 100644 build/lib/docx/text/__init__.py delete mode 100644 build/lib/docx/text/font.py delete mode 100644 build/lib/docx/text/paragraph.py delete mode 100644 build/lib/docx/text/parfmt.py delete mode 100644 build/lib/docx/text/run.py delete mode 100644 build/lib/docx/text/tabstops.py diff --git a/.gitignore b/.gitignore index de25a6f76..56a394e03 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ _scratch/ Session.vim /.tox/ +/build/ \ No newline at end of file diff --git a/build/lib/docx/__init__.py b/build/lib/docx/__init__.py deleted file mode 100644 index a876023a7..000000000 --- a/build/lib/docx/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# encoding: utf-8 - -from docx.api import Document # noqa - -__version__ = '0.1.7' - - -# register custom Part classes with opc package reader - -from docx.opc.constants import CONTENT_TYPE as CT, RELATIONSHIP_TYPE as RT -from docx.opc.part import PartFactory -from docx.opc.parts.coreprops import CorePropertiesPart - -from docx.parts.document import DocumentPart -from docx.parts.image import ImagePart -from docx.parts.numbering import NumberingPart -from docx.parts.settings import SettingsPart -from docx.parts.styles import StylesPart -from docx.parts.comments import CommentsPart -from docx.parts.footnotes import FootnotesPart - - -def part_class_selector(content_type, reltype): - if reltype == RT.IMAGE: - return ImagePart - return None - - -PartFactory.part_class_selector = part_class_selector -PartFactory.part_type_for[CT.WML_COMMENTS] = CommentsPart -PartFactory.part_type_for[CT.OPC_CORE_PROPERTIES] = CorePropertiesPart -PartFactory.part_type_for[CT.WML_DOCUMENT_MAIN] = DocumentPart -PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart -PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart -PartFactory.part_type_for[CT.WML_STYLES] = StylesPart -PartFactory.part_type_for[CT.WML_FOOTNOTES] = FootnotesPart - -del ( - CT, CorePropertiesPart, FootnotesPart, CommentsPart, DocumentPart, NumberingPart, PartFactory, - StylesPart, part_class_selector, -) diff --git a/build/lib/docx/api.py b/build/lib/docx/api.py deleted file mode 100644 index 63e18c406..000000000 --- a/build/lib/docx/api.py +++ /dev/null @@ -1,37 +0,0 @@ -# encoding: utf-8 - -""" -Directly exposed API functions and classes, :func:`Document` for now. -Provides a syntactically more convenient API for interacting with the -OpcPackage graph. -""" - -from __future__ import absolute_import, division, print_function - -import os - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.package import Package - - -def Document(docx=None): - """ - Return a |Document| object loaded from *docx*, where *docx* can be - either a path to a ``.docx`` file (a string) or a file-like object. If - *docx* is missing or ``None``, the built-in default document "template" - is loaded. - """ - docx = _default_docx_path() if docx is None else docx - document_part = Package.open(docx).main_document_part - if document_part.content_type != CT.WML_DOCUMENT_MAIN: - tmpl = "file '%s' is not a Word file, content type is '%s'" - raise ValueError(tmpl % (docx, document_part.content_type)) - return document_part.document - - -def _default_docx_path(): - """ - Return the path to the built-in default .docx package. - """ - _thisdir = os.path.split(__file__)[0] - return os.path.join(_thisdir, 'templates', 'default.docx') diff --git a/build/lib/docx/blkcntnr.py b/build/lib/docx/blkcntnr.py deleted file mode 100644 index d57a0cd0f..000000000 --- a/build/lib/docx/blkcntnr.py +++ /dev/null @@ -1,74 +0,0 @@ -# encoding: utf-8 - -""" -Block item container, used by body, cell, header, etc. Block level items are -things like paragraph and table, although there are a few other specialized -ones like structured document tags. -""" - -from __future__ import absolute_import, print_function - -from .oxml.table import CT_Tbl -from .shared import Parented -from .text.paragraph import Paragraph - - -class BlockItemContainer(Parented): - """ - Base class for proxy objects that can contain block items, such as _Body, - _Cell, header, footer, footnote, endnote, comment, and text box objects. - Provides the shared functionality to add a block item like a paragraph or - table. - """ - def __init__(self, element, parent): - super(BlockItemContainer, self).__init__(parent) - self._element = element - - def add_paragraph(self, text='', style=None): - """ - Return a paragraph newly added to the end of the content in this - container, having *text* in a single run if present, and having - paragraph style *style*. If *style* is |None|, no paragraph style is - applied, which has the same effect as applying the 'Normal' style. - """ - paragraph = self._add_paragraph() - if text: - paragraph.add_run(text) - if style is not None: - paragraph.style = style - return paragraph - - def add_table(self, rows, cols, width): - """ - Return a table of *width* having *rows* rows and *cols* columns, - newly appended to the content in this container. *width* is evenly - distributed between the table columns. - """ - from .table import Table - tbl = CT_Tbl.new_tbl(rows, cols, width) - self._element._insert_tbl(tbl) - return Table(tbl, self) - - @property - def paragraphs(self): - """ - A list containing the paragraphs in this container, in document - order. Read-only. - """ - return [Paragraph(p, self) for p in self._element.p_lst] - - @property - def tables(self): - """ - A list containing the tables in this container, in document order. - Read-only. - """ - from .table import Table - return [Table(tbl, self) for tbl in self._element.tbl_lst] - - def _add_paragraph(self): - """ - Return a paragraph newly added to the end of the content in this - container. - """ - return Paragraph(self._element.add_p(), self) diff --git a/build/lib/docx/compat.py b/build/lib/docx/compat.py deleted file mode 100644 index dc9e20e39..000000000 --- a/build/lib/docx/compat.py +++ /dev/null @@ -1,43 +0,0 @@ -# encoding: utf-8 - -""" -Provides Python 2/3 compatibility objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -import sys - -# =========================================================================== -# Python 3 versions -# =========================================================================== - -if sys.version_info >= (3, 0): - - from io import BytesIO - - def is_string(obj): - """ - Return True if *obj* is a string, False otherwise. - """ - return isinstance(obj, str) - - Unicode = str - -# =========================================================================== -# Python 2 versions -# =========================================================================== - -else: - - from StringIO import StringIO as BytesIO # noqa - - def is_string(obj): - """ - Return True if *obj* is a string, False otherwise. - """ - return isinstance(obj, basestring) - - Unicode = unicode diff --git a/build/lib/docx/dml/__init__.py b/build/lib/docx/dml/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/lib/docx/dml/color.py b/build/lib/docx/dml/color.py deleted file mode 100644 index 2f2f25cb2..000000000 --- a/build/lib/docx/dml/color.py +++ /dev/null @@ -1,116 +0,0 @@ -# encoding: utf-8 - -""" -DrawingML objects related to color, ColorFormat being the most prominent. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..enum.dml import MSO_COLOR_TYPE -from ..oxml.simpletypes import ST_HexColorAuto -from ..shared import ElementProxy - - -class ColorFormat(ElementProxy): - """ - Provides access to color settings such as RGB color, theme color, and - luminance adjustments. - """ - - __slots__ = () - - def __init__(self, rPr_parent): - super(ColorFormat, self).__init__(rPr_parent) - - @property - def rgb(self): - """ - An |RGBColor| value or |None| if no RGB color is specified. - - When :attr:`type` is `MSO_COLOR_TYPE.RGB`, the value of this property - will always be an |RGBColor| value. It may also be an |RGBColor| - value if :attr:`type` is `MSO_COLOR_TYPE.THEME`, as Word writes the - current value of a theme color when one is assigned. In that case, - the RGB value should be interpreted as no more than a good guess - however, as the theme color takes precedence at rendering time. Its - value is |None| whenever :attr:`type` is either |None| or - `MSO_COLOR_TYPE.AUTO`. - - Assigning an |RGBColor| value causes :attr:`type` to become - `MSO_COLOR_TYPE.RGB` and any theme color is removed. Assigning |None| - causes any color to be removed such that the effective color is - inherited from the style hierarchy. - """ - color = self._color - if color is None: - return None - if color.val == ST_HexColorAuto.AUTO: - return None - return color.val - - @rgb.setter - def rgb(self, value): - if value is None and self._color is None: - return - rPr = self._element.get_or_add_rPr() - rPr._remove_color() - if value is not None: - rPr.get_or_add_color().val = value - - @property - def theme_color(self): - """ - A member of :ref:`MsoThemeColorIndex` or |None| if no theme color is - specified. When :attr:`type` is `MSO_COLOR_TYPE.THEME`, the value of - this property will always be a member of :ref:`MsoThemeColorIndex`. - When :attr:`type` has any other value, the value of this property is - |None|. - - Assigning a member of :ref:`MsoThemeColorIndex` causes :attr:`type` - to become `MSO_COLOR_TYPE.THEME`. Any existing RGB value is retained - but ignored by Word. Assigning |None| causes any color specification - to be removed such that the effective color is inherited from the - style hierarchy. - """ - color = self._color - if color is None or color.themeColor is None: - return None - return color.themeColor - - @theme_color.setter - def theme_color(self, value): - if value is None: - if self._color is not None: - self._element.rPr._remove_color() - return - self._element.get_or_add_rPr().get_or_add_color().themeColor = value - - @property - def type(self): - """ - Read-only. A member of :ref:`MsoColorType`, one of RGB, THEME, or - AUTO, corresponding to the way this color is defined. Its value is - |None| if no color is applied at this level, which causes the - effective color to be inherited from the style hierarchy. - """ - color = self._color - if color is None: - return None - if color.themeColor is not None: - return MSO_COLOR_TYPE.THEME - if color.val == ST_HexColorAuto.AUTO: - return MSO_COLOR_TYPE.AUTO - return MSO_COLOR_TYPE.RGB - - @property - def _color(self): - """ - Return `w:rPr/w:color` or |None| if not present. Helper to factor out - repetitive element access. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.color diff --git a/build/lib/docx/document.py b/build/lib/docx/document.py deleted file mode 100644 index f3b2e689f..000000000 --- a/build/lib/docx/document.py +++ /dev/null @@ -1,233 +0,0 @@ -# encoding: utf-8 - -""" -|Document| and closely related objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from .blkcntnr import BlockItemContainer -from .enum.section import WD_SECTION -from .enum.text import WD_BREAK -from .section import Section, Sections -from .shared import ElementProxy, Emu - - -class Document(ElementProxy): - """ - WordprocessingML (WML) document. Not intended to be constructed directly. - Use :func:`docx.Document` to open or create a document. - """ - - __slots__ = ('_part', '__body') - - def __init__(self, element, part): - super(Document, self).__init__(element) - self._part = part - self.__body = None - - def add_heading(self, text='', level=1): - """ - Return a heading paragraph newly added to the end of the document, - containing *text* and having its paragraph style determined by - *level*. If *level* is 0, the style is set to `Title`. If *level* is - 1 (or omitted), `Heading 1` is used. Otherwise the style is set to - `Heading {level}`. Raises |ValueError| if *level* is outside the - range 0-9. - """ - if not 0 <= level <= 9: - raise ValueError("level must be in range 0-9, got %d" % level) - style = 'Title' if level == 0 else 'Heading %d' % level - return self.add_paragraph(text, style) - - def add_page_break(self): - """ - Return a paragraph newly added to the end of the document and - containing only a page break. - """ - paragraph = self.add_paragraph() - paragraph.add_run().add_break(WD_BREAK.PAGE) - return paragraph - - def add_paragraph(self, text='', style=None): - """ - Return a paragraph newly added to the end of the document, populated - with *text* and having paragraph style *style*. *text* can contain - tab (``\\t``) characters, which are converted to the appropriate XML - form for a tab. *text* can also include newline (``\\n``) or carriage - return (``\\r``) characters, each of which is converted to a line - break. - """ - return self._body.add_paragraph(text, style) - - def add_picture(self, image_path_or_stream, width=None, height=None): - """ - Return a new picture shape added in its own paragraph at the end of - the document. The picture contains the image at - *image_path_or_stream*, scaled based on *width* and *height*. If - neither width nor height is specified, the picture appears at its - native size. If only one is specified, it is used to compute - a scaling factor that is then applied to the unspecified dimension, - preserving the aspect ratio of the image. The native size of the - picture is calculated using the dots-per-inch (dpi) value specified - in the image file, defaulting to 72 dpi if no value is specified, as - is often the case. - """ - run = self.add_paragraph().add_run() - return run.add_picture(image_path_or_stream, width, height) - - def add_section(self, start_type=WD_SECTION.NEW_PAGE): - """ - Return a |Section| object representing a new section added at the end - of the document. The optional *start_type* argument must be a member - of the :ref:`WdSectionStart` enumeration, and defaults to - ``WD_SECTION.NEW_PAGE`` if not provided. - """ - new_sectPr = self._element.body.add_section_break() - new_sectPr.start_type = start_type - return Section(new_sectPr) - - def add_table(self, rows, cols, style=None): - """ - Add a table having row and column counts of *rows* and *cols* - respectively and table style of *style*. *style* may be a paragraph - style object or a paragraph style name. If *style* is |None|, the - table inherits the default table style of the document. - """ - table = self._body.add_table(rows, cols, self._block_width) - table.style = style - return table - - @property - def core_properties(self): - """ - A |CoreProperties| object providing read/write access to the core - properties of this document. - """ - return self._part.core_properties - - @property - def comments_part(self): - """ - A |Comments| object providing read/write access to the core - properties of this document. - """ - return self.part.comments_part - - # @property - # def footnotes_part(self): - # """ - # A |Footnotes| object providing read/write access to the core - # properties of this document. - # """ - # return self.part._footnotes_part - - - - @property - def inline_shapes(self): - """ - An |InlineShapes| object providing access to the inline shapes in - this document. An inline shape is a graphical object, such as - a picture, contained in a run of text and behaving like a character - glyph, being flowed like other text in a paragraph. - """ - return self._part.inline_shapes - - @property - def paragraphs(self): - """ - A list of |Paragraph| instances corresponding to the paragraphs in - the document, in document order. Note that paragraphs within revision - marks such as ```` or ```` do not appear in this list. - """ - return self._body.paragraphs - - @property - def part(self): - """ - The |DocumentPart| object of this document. - """ - return self._part - - def save(self, path_or_stream): - """ - Save this document to *path_or_stream*, which can be either a path to - a filesystem location (a string) or a file-like object. - """ - self._part.save(path_or_stream) - - @property - def sections(self): - """ - A |Sections| object providing access to each section in this - document. - """ - return Sections(self._element) - - @property - def settings(self): - """ - A |Settings| object providing access to the document-level settings - for this document. - """ - return self._part.settings - - @property - def styles(self): - """ - A |Styles| object providing access to the styles in this document. - """ - return self._part.styles - - @property - def tables(self): - """ - A list of |Table| instances corresponding to the tables in the - document, in document order. Note that only tables appearing at the - top level of the document appear in this list; a table nested inside - a table cell does not appear. A table within revision marks such as - ```` or ```` will also not appear in the list. - """ - return self._body.tables - - @property - def _block_width(self): - """ - Return a |Length| object specifying the width of available "writing" - space between the margins of the last section of this document. - """ - section = self.sections[-1] - return Emu( - section.page_width - section.left_margin - section.right_margin - ) - - @property - def _body(self): - """ - The |_Body| instance containing the content for this document. - """ - if self.__body is None: - self.__body = _Body(self._element.body, self) - return self.__body - - -class _Body(BlockItemContainer): - """ - Proxy for ```` element in this document, having primarily a - container role. - """ - def __init__(self, body_elm, parent): - super(_Body, self).__init__(body_elm, parent) - self._body = body_elm - - def clear_content(self): - """ - Return this |_Body| instance after clearing it of all content. - Section properties for the main document story, if present, are - preserved. - """ - self._body.clear_content() - return self diff --git a/build/lib/docx/enum/__init__.py b/build/lib/docx/enum/__init__.py deleted file mode 100644 index dd49faafd..000000000 --- a/build/lib/docx/enum/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations used in python-docx -""" - -from __future__ import absolute_import, print_function, unicode_literals - - -class Enumeration(object): - - @classmethod - def from_xml(cls, xml_val): - return cls._xml_to_idx[xml_val] - - @classmethod - def to_xml(cls, enum_val): - return cls._idx_to_xml[enum_val] diff --git a/build/lib/docx/enum/base.py b/build/lib/docx/enum/base.py deleted file mode 100644 index 36764b1a6..000000000 --- a/build/lib/docx/enum/base.py +++ /dev/null @@ -1,363 +0,0 @@ -# encoding: utf-8 - -""" -Base classes and other objects used by enumerations -""" - -from __future__ import absolute_import, print_function - -import sys -import textwrap - -from ..exceptions import InvalidXmlError - - -def alias(*aliases): - """ - Decorating a class with @alias('FOO', 'BAR', ..) allows the class to - be referenced by each of the names provided as arguments. - """ - def decorator(cls): - # alias must be set in globals from caller's frame - caller = sys._getframe(1) - globals_dict = caller.f_globals - for alias in aliases: - globals_dict[alias] = cls - return cls - return decorator - - -class _DocsPageFormatter(object): - """Generate an .rst doc page for an enumeration. - - Formats a RestructuredText documention page (string) for the enumeration - class parts passed to the constructor. An immutable one-shot service - object. - """ - - def __init__(self, clsname, clsdict): - self._clsname = clsname - self._clsdict = clsdict - - @property - def page_str(self): - """ - The RestructuredText documentation page for the enumeration. This is - the only API member for the class. - """ - tmpl = '.. _%s:\n\n%s\n\n%s\n\n----\n\n%s' - components = ( - self._ms_name, self._page_title, self._intro_text, - self._member_defs - ) - return tmpl % components - - @property - def _intro_text(self): - """Docstring of the enumeration, formatted for documentation page.""" - try: - cls_docstring = self._clsdict['__doc__'] - except KeyError: - cls_docstring = '' - - if cls_docstring is None: - return '' - - return textwrap.dedent(cls_docstring).strip() - - def _member_def(self, member): - """ - Return an individual member definition formatted as an RST glossary - entry, wrapped to fit within 78 columns. - """ - member_docstring = textwrap.dedent(member.docstring).strip() - member_docstring = textwrap.fill( - member_docstring, width=78, initial_indent=' '*4, - subsequent_indent=' '*4 - ) - return '%s\n%s\n' % (member.name, member_docstring) - - @property - def _member_defs(self): - """ - A single string containing the aggregated member definitions section - of the documentation page - """ - members = self._clsdict['__members__'] - member_defs = [ - self._member_def(member) for member in members - if member.name is not None - ] - return '\n'.join(member_defs) - - @property - def _ms_name(self): - """ - The Microsoft API name for this enumeration - """ - return self._clsdict['__ms_name__'] - - @property - def _page_title(self): - """ - The title for the documentation page, formatted as code (surrounded - in double-backtics) and underlined with '=' characters - """ - title_underscore = '=' * (len(self._clsname)+4) - return '``%s``\n%s' % (self._clsname, title_underscore) - - -class MetaEnumeration(type): - """ - The metaclass for Enumeration and its subclasses. Adds a name for each - named member and compiles state needed by the enumeration class to - respond to other attribute gets - """ - def __new__(meta, clsname, bases, clsdict): - meta._add_enum_members(clsdict) - meta._collect_valid_settings(clsdict) - meta._generate_docs_page(clsname, clsdict) - return type.__new__(meta, clsname, bases, clsdict) - - @classmethod - def _add_enum_members(meta, clsdict): - """ - Dispatch ``.add_to_enum()`` call to each member so it can do its - thing to properly add itself to the enumeration class. This - delegation allows member sub-classes to add specialized behaviors. - """ - enum_members = clsdict['__members__'] - for member in enum_members: - member.add_to_enum(clsdict) - - @classmethod - def _collect_valid_settings(meta, clsdict): - """ - Return a sequence containing the enumeration values that are valid - assignment values. Return-only values are excluded. - """ - enum_members = clsdict['__members__'] - valid_settings = [] - for member in enum_members: - valid_settings.extend(member.valid_settings) - clsdict['_valid_settings'] = valid_settings - - @classmethod - def _generate_docs_page(meta, clsname, clsdict): - """ - Return the RST documentation page for the enumeration. - """ - clsdict['__docs_rst__'] = ( - _DocsPageFormatter(clsname, clsdict).page_str - ) - - -class EnumerationBase(object): - """ - Base class for all enumerations, used directly for enumerations requiring - only basic behavior. It's __dict__ is used below in the Python 2+3 - compatible metaclass definition. - """ - __members__ = () - __ms_name__ = '' - - @classmethod - def validate(cls, value): - """ - Raise |ValueError| if *value* is not an assignable value. - """ - if value not in cls._valid_settings: - raise ValueError( - "%s not a member of %s enumeration" % (value, cls.__name__) - ) - - -Enumeration = MetaEnumeration( - 'Enumeration', (object,), dict(EnumerationBase.__dict__) -) - - -class XmlEnumeration(Enumeration): - """ - Provides ``to_xml()`` and ``from_xml()`` methods in addition to base - enumeration features - """ - __members__ = () - __ms_name__ = '' - - @classmethod - def from_xml(cls, xml_val): - """ - Return the enumeration member corresponding to the XML value - *xml_val*. - """ - if xml_val not in cls._xml_to_member: - raise InvalidXmlError( - "attribute value '%s' not valid for this type" % xml_val - ) - return cls._xml_to_member[xml_val] - - @classmethod - def to_xml(cls, enum_val): - """ - Return the XML value of the enumeration value *enum_val*. - """ - if enum_val not in cls._member_to_xml: - raise ValueError( - "value '%s' not in enumeration %s" % (enum_val, cls.__name__) - ) - return cls._member_to_xml[enum_val] - - -class EnumMember(object): - """ - Used in the enumeration class definition to define a member value and its - mappings - """ - def __init__(self, name, value, docstring): - self._name = name - if isinstance(value, int): - value = EnumValue(name, value, docstring) - self._value = value - self._docstring = docstring - - def add_to_enum(self, clsdict): - """ - Add a name to *clsdict* for this member. - """ - self.register_name(clsdict) - - @property - def docstring(self): - """ - The description of this member - """ - return self._docstring - - @property - def name(self): - """ - The distinguishing name of this member within the enumeration class, - e.g. 'MIDDLE' for MSO_VERTICAL_ANCHOR.MIDDLE, if this is a named - member. Otherwise the primitive value such as |None|, |True| or - |False|. - """ - return self._name - - def register_name(self, clsdict): - """ - Add a member name to the class dict *clsdict* containing the value of - this member object. Where the name of this object is None, do - nothing; this allows out-of-band values to be defined without adding - a name to the class dict. - """ - if self.name is None: - return - clsdict[self.name] = self.value - - @property - def valid_settings(self): - """ - A sequence containing the values valid for assignment for this - member. May be zero, one, or more in number. - """ - return (self._value,) - - @property - def value(self): - """ - The enumeration value for this member, often an instance of - EnumValue, but may be a primitive value such as |None|. - """ - return self._value - - -class EnumValue(int): - """ - A named enumeration value, providing __str__ and __doc__ string values - for its symbolic name and description, respectively. Subclasses int, so - behaves as a regular int unless the strings are asked for. - """ - def __new__(cls, member_name, int_value, docstring): - return super(EnumValue, cls).__new__(cls, int_value) - - def __init__(self, member_name, int_value, docstring): - super(EnumValue, self).__init__() - self._member_name = member_name - self._docstring = docstring - - @property - def __doc__(self): - """ - The description of this enumeration member - """ - return self._docstring.strip() - - def __str__(self): - """ - The symbolic name and string value of this member, e.g. 'MIDDLE (3)' - """ - return "%s (%d)" % (self._member_name, int(self)) - - -class ReturnValueOnlyEnumMember(EnumMember): - """ - Used to define a member of an enumeration that is only valid as a query - result and is not valid as a setting, e.g. MSO_VERTICAL_ANCHOR.MIXED (-2) - """ - @property - def valid_settings(self): - """ - No settings are valid for a return-only value. - """ - return () - - -class XmlMappedEnumMember(EnumMember): - """ - Used to define a member whose value maps to an XML attribute value. - """ - def __init__(self, name, value, xml_value, docstring): - super(XmlMappedEnumMember, self).__init__(name, value, docstring) - self._xml_value = xml_value - - def add_to_enum(self, clsdict): - """ - Compile XML mappings in addition to base add behavior. - """ - super(XmlMappedEnumMember, self).add_to_enum(clsdict) - self.register_xml_mapping(clsdict) - - def register_xml_mapping(self, clsdict): - """ - Add XML mappings to the enumeration class state for this member. - """ - member_to_xml = self._get_or_add_member_to_xml(clsdict) - member_to_xml[self.value] = self.xml_value - xml_to_member = self._get_or_add_xml_to_member(clsdict) - xml_to_member[self.xml_value] = self.value - - @property - def xml_value(self): - """ - The XML attribute value that corresponds to this enumeration value - """ - return self._xml_value - - @staticmethod - def _get_or_add_member_to_xml(clsdict): - """ - Add the enum -> xml value mapping to the enumeration class state - """ - if '_member_to_xml' not in clsdict: - clsdict['_member_to_xml'] = dict() - return clsdict['_member_to_xml'] - - @staticmethod - def _get_or_add_xml_to_member(clsdict): - """ - Add the xml -> enum value mapping to the enumeration class state - """ - if '_xml_to_member' not in clsdict: - clsdict['_xml_to_member'] = dict() - return clsdict['_xml_to_member'] diff --git a/build/lib/docx/enum/dml.py b/build/lib/docx/enum/dml.py deleted file mode 100644 index 1ad0eaa87..000000000 --- a/build/lib/docx/enum/dml.py +++ /dev/null @@ -1,124 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations used by DrawingML objects -""" - -from __future__ import absolute_import - -from .base import ( - alias, Enumeration, EnumMember, XmlEnumeration, XmlMappedEnumMember -) - - -class MSO_COLOR_TYPE(Enumeration): - """ - Specifies the color specification scheme - - Example:: - - from docx.enum.dml import MSO_COLOR_TYPE - - assert font.color.type == MSO_COLOR_TYPE.SCHEME - """ - - __ms_name__ = 'MsoColorType' - - __url__ = ( - 'http://msdn.microsoft.com/en-us/library/office/ff864912(v=office.15' - ').aspx' - ) - - __members__ = ( - EnumMember( - 'RGB', 1, 'Color is specified by an |RGBColor| value.' - ), - EnumMember( - 'THEME', 2, 'Color is one of the preset theme colors.' - ), - EnumMember( - 'AUTO', 101, 'Color is determined automatically by the ' - 'application.' - ), - ) - - -@alias('MSO_THEME_COLOR') -class MSO_THEME_COLOR_INDEX(XmlEnumeration): - """ - Indicates the Office theme color, one of those shown in the color gallery - on the formatting ribbon. - - Alias: ``MSO_THEME_COLOR`` - - Example:: - - from docx.enum.dml import MSO_THEME_COLOR - - font.color.theme_color = MSO_THEME_COLOR.ACCENT_1 - """ - - __ms_name__ = 'MsoThemeColorIndex' - - __url__ = ( - 'http://msdn.microsoft.com/en-us/library/office/ff860782(v=office.15' - ').aspx' - ) - - __members__ = ( - EnumMember( - 'NOT_THEME_COLOR', 0, 'Indicates the color is not a theme color.' - ), - XmlMappedEnumMember( - 'ACCENT_1', 5, 'accent1', 'Specifies the Accent 1 theme color.' - ), - XmlMappedEnumMember( - 'ACCENT_2', 6, 'accent2', 'Specifies the Accent 2 theme color.' - ), - XmlMappedEnumMember( - 'ACCENT_3', 7, 'accent3', 'Specifies the Accent 3 theme color.' - ), - XmlMappedEnumMember( - 'ACCENT_4', 8, 'accent4', 'Specifies the Accent 4 theme color.' - ), - XmlMappedEnumMember( - 'ACCENT_5', 9, 'accent5', 'Specifies the Accent 5 theme color.' - ), - XmlMappedEnumMember( - 'ACCENT_6', 10, 'accent6', 'Specifies the Accent 6 theme color.' - ), - XmlMappedEnumMember( - 'BACKGROUND_1', 14, 'background1', 'Specifies the Background 1 ' - 'theme color.' - ), - XmlMappedEnumMember( - 'BACKGROUND_2', 16, 'background2', 'Specifies the Background 2 ' - 'theme color.' - ), - XmlMappedEnumMember( - 'DARK_1', 1, 'dark1', 'Specifies the Dark 1 theme color.' - ), - XmlMappedEnumMember( - 'DARK_2', 3, 'dark2', 'Specifies the Dark 2 theme color.' - ), - XmlMappedEnumMember( - 'FOLLOWED_HYPERLINK', 12, 'followedHyperlink', 'Specifies the ' - 'theme color for a clicked hyperlink.' - ), - XmlMappedEnumMember( - 'HYPERLINK', 11, 'hyperlink', 'Specifies the theme color for a ' - 'hyperlink.' - ), - XmlMappedEnumMember( - 'LIGHT_1', 2, 'light1', 'Specifies the Light 1 theme color.' - ), - XmlMappedEnumMember( - 'LIGHT_2', 4, 'light2', 'Specifies the Light 2 theme color.' - ), - XmlMappedEnumMember( - 'TEXT_1', 13, 'text1', 'Specifies the Text 1 theme color.' - ), - XmlMappedEnumMember( - 'TEXT_2', 15, 'text2', 'Specifies the Text 2 theme color.' - ), - ) diff --git a/build/lib/docx/enum/section.py b/build/lib/docx/enum/section.py deleted file mode 100644 index b16ddbe72..000000000 --- a/build/lib/docx/enum/section.py +++ /dev/null @@ -1,76 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations related to the main document in WordprocessingML files -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from .base import alias, XmlEnumeration, XmlMappedEnumMember - - -@alias('WD_ORIENT') -class WD_ORIENTATION(XmlEnumeration): - """ - alias: **WD_ORIENT** - - Specifies the page layout orientation. - - Example:: - - from docx.enum.section import WD_ORIENT - - section = document.sections[-1] - section.orientation = WD_ORIENT.LANDSCAPE - """ - - __ms_name__ = 'WdOrientation' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff837902.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'PORTRAIT', 0, 'portrait', 'Portrait orientation.' - ), - XmlMappedEnumMember( - 'LANDSCAPE', 1, 'landscape', 'Landscape orientation.' - ), - ) - - -@alias('WD_SECTION') -class WD_SECTION_START(XmlEnumeration): - """ - alias: **WD_SECTION** - - Specifies the start type of a section break. - - Example:: - - from docx.enum.section import WD_SECTION - - section = document.sections[0] - section.start_type = WD_SECTION.NEW_PAGE - """ - - __ms_name__ = 'WdSectionStart' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff840975.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'CONTINUOUS', 0, 'continuous', 'Continuous section break.' - ), - XmlMappedEnumMember( - 'NEW_COLUMN', 1, 'nextColumn', 'New column section break.' - ), - XmlMappedEnumMember( - 'NEW_PAGE', 2, 'nextPage', 'New page section break.' - ), - XmlMappedEnumMember( - 'EVEN_PAGE', 3, 'evenPage', 'Even pages section break.' - ), - XmlMappedEnumMember( - 'ODD_PAGE', 4, 'oddPage', 'Section begins on next odd page.' - ), - ) diff --git a/build/lib/docx/enum/shape.py b/build/lib/docx/enum/shape.py deleted file mode 100644 index f1d6ffd8c..000000000 --- a/build/lib/docx/enum/shape.py +++ /dev/null @@ -1,21 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations related to DrawingML shapes in WordprocessingML files -""" - -from __future__ import absolute_import, print_function, unicode_literals - - -class WD_INLINE_SHAPE_TYPE(object): - """ - Corresponds to WdInlineShapeType enumeration - http://msdn.microsoft.com/en-us/library/office/ff192587.aspx - """ - CHART = 12 - LINKED_PICTURE = 4 - PICTURE = 3 - SMART_ART = 15 - NOT_IMPLEMENTED = -6 - -WD_INLINE_SHAPE = WD_INLINE_SHAPE_TYPE diff --git a/build/lib/docx/enum/style.py b/build/lib/docx/enum/style.py deleted file mode 100644 index 515c594ce..000000000 --- a/build/lib/docx/enum/style.py +++ /dev/null @@ -1,466 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations related to styles -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from .base import alias, EnumMember, XmlEnumeration, XmlMappedEnumMember - - -@alias('WD_STYLE') -class WD_BUILTIN_STYLE(XmlEnumeration): - """ - alias: **WD_STYLE** - - Specifies a built-in Microsoft Word style. - - Example:: - - from docx import Document - from docx.enum.style import WD_STYLE - - document = Document() - styles = document.styles - style = styles[WD_STYLE.BODY_TEXT] - """ - - __ms_name__ = 'WdBuiltinStyle' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff835210.aspx' - - __members__ = ( - EnumMember( - 'BLOCK_QUOTATION', -85, 'Block Text.' - ), - EnumMember( - 'BODY_TEXT', -67, 'Body Text.' - ), - EnumMember( - 'BODY_TEXT_2', -81, 'Body Text 2.' - ), - EnumMember( - 'BODY_TEXT_3', -82, 'Body Text 3.' - ), - EnumMember( - 'BODY_TEXT_FIRST_INDENT', -78, 'Body Text First Indent.' - ), - EnumMember( - 'BODY_TEXT_FIRST_INDENT_2', -79, 'Body Text First Indent 2.' - ), - EnumMember( - 'BODY_TEXT_INDENT', -68, 'Body Text Indent.' - ), - EnumMember( - 'BODY_TEXT_INDENT_2', -83, 'Body Text Indent 2.' - ), - EnumMember( - 'BODY_TEXT_INDENT_3', -84, 'Body Text Indent 3.' - ), - EnumMember( - 'BOOK_TITLE', -265, 'Book Title.' - ), - EnumMember( - 'CAPTION', -35, 'Caption.' - ), - EnumMember( - 'CLOSING', -64, 'Closing.' - ), - EnumMember( - 'COMMENT_REFERENCE', -40, 'Comment Reference.' - ), - EnumMember( - 'COMMENT_TEXT', -31, 'Comment Text.' - ), - EnumMember( - 'DATE', -77, 'Date.' - ), - EnumMember( - 'DEFAULT_PARAGRAPH_FONT', -66, 'Default Paragraph Font.' - ), - EnumMember( - 'EMPHASIS', -89, 'Emphasis.' - ), - EnumMember( - 'ENDNOTE_REFERENCE', -43, 'Endnote Reference.' - ), - EnumMember( - 'ENDNOTE_TEXT', -44, 'Endnote Text.' - ), - EnumMember( - 'ENVELOPE_ADDRESS', -37, 'Envelope Address.' - ), - EnumMember( - 'ENVELOPE_RETURN', -38, 'Envelope Return.' - ), - EnumMember( - 'FOOTER', -33, 'Footer.' - ), - EnumMember( - 'FOOTNOTE_REFERENCE', -39, 'Footnote Reference.' - ), - EnumMember( - 'FOOTNOTE_TEXT', -30, 'Footnote Text.' - ), - EnumMember( - 'HEADER', -32, 'Header.' - ), - EnumMember( - 'HEADING_1', -2, 'Heading 1.' - ), - EnumMember( - 'HEADING_2', -3, 'Heading 2.' - ), - EnumMember( - 'HEADING_3', -4, 'Heading 3.' - ), - EnumMember( - 'HEADING_4', -5, 'Heading 4.' - ), - EnumMember( - 'HEADING_5', -6, 'Heading 5.' - ), - EnumMember( - 'HEADING_6', -7, 'Heading 6.' - ), - EnumMember( - 'HEADING_7', -8, 'Heading 7.' - ), - EnumMember( - 'HEADING_8', -9, 'Heading 8.' - ), - EnumMember( - 'HEADING_9', -10, 'Heading 9.' - ), - EnumMember( - 'HTML_ACRONYM', -96, 'HTML Acronym.' - ), - EnumMember( - 'HTML_ADDRESS', -97, 'HTML Address.' - ), - EnumMember( - 'HTML_CITE', -98, 'HTML Cite.' - ), - EnumMember( - 'HTML_CODE', -99, 'HTML Code.' - ), - EnumMember( - 'HTML_DFN', -100, 'HTML Definition.' - ), - EnumMember( - 'HTML_KBD', -101, 'HTML Keyboard.' - ), - EnumMember( - 'HTML_NORMAL', -95, 'Normal (Web).' - ), - EnumMember( - 'HTML_PRE', -102, 'HTML Preformatted.' - ), - EnumMember( - 'HTML_SAMP', -103, 'HTML Sample.' - ), - EnumMember( - 'HTML_TT', -104, 'HTML Typewriter.' - ), - EnumMember( - 'HTML_VAR', -105, 'HTML Variable.' - ), - EnumMember( - 'HYPERLINK', -86, 'Hyperlink.' - ), - EnumMember( - 'HYPERLINK_FOLLOWED', -87, 'Followed Hyperlink.' - ), - EnumMember( - 'INDEX_1', -11, 'Index 1.' - ), - EnumMember( - 'INDEX_2', -12, 'Index 2.' - ), - EnumMember( - 'INDEX_3', -13, 'Index 3.' - ), - EnumMember( - 'INDEX_4', -14, 'Index 4.' - ), - EnumMember( - 'INDEX_5', -15, 'Index 5.' - ), - EnumMember( - 'INDEX_6', -16, 'Index 6.' - ), - EnumMember( - 'INDEX_7', -17, 'Index 7.' - ), - EnumMember( - 'INDEX_8', -18, 'Index 8.' - ), - EnumMember( - 'INDEX_9', -19, 'Index 9.' - ), - EnumMember( - 'INDEX_HEADING', -34, 'Index Heading' - ), - EnumMember( - 'INTENSE_EMPHASIS', -262, 'Intense Emphasis.' - ), - EnumMember( - 'INTENSE_QUOTE', -182, 'Intense Quote.' - ), - EnumMember( - 'INTENSE_REFERENCE', -264, 'Intense Reference.' - ), - EnumMember( - 'LINE_NUMBER', -41, 'Line Number.' - ), - EnumMember( - 'LIST', -48, 'List.' - ), - EnumMember( - 'LIST_2', -51, 'List 2.' - ), - EnumMember( - 'LIST_3', -52, 'List 3.' - ), - EnumMember( - 'LIST_4', -53, 'List 4.' - ), - EnumMember( - 'LIST_5', -54, 'List 5.' - ), - EnumMember( - 'LIST_BULLET', -49, 'List Bullet.' - ), - EnumMember( - 'LIST_BULLET_2', -55, 'List Bullet 2.' - ), - EnumMember( - 'LIST_BULLET_3', -56, 'List Bullet 3.' - ), - EnumMember( - 'LIST_BULLET_4', -57, 'List Bullet 4.' - ), - EnumMember( - 'LIST_BULLET_5', -58, 'List Bullet 5.' - ), - EnumMember( - 'LIST_CONTINUE', -69, 'List Continue.' - ), - EnumMember( - 'LIST_CONTINUE_2', -70, 'List Continue 2.' - ), - EnumMember( - 'LIST_CONTINUE_3', -71, 'List Continue 3.' - ), - EnumMember( - 'LIST_CONTINUE_4', -72, 'List Continue 4.' - ), - EnumMember( - 'LIST_CONTINUE_5', -73, 'List Continue 5.' - ), - EnumMember( - 'LIST_NUMBER', -50, 'List Number.' - ), - EnumMember( - 'LIST_NUMBER_2', -59, 'List Number 2.' - ), - EnumMember( - 'LIST_NUMBER_3', -60, 'List Number 3.' - ), - EnumMember( - 'LIST_NUMBER_4', -61, 'List Number 4.' - ), - EnumMember( - 'LIST_NUMBER_5', -62, 'List Number 5.' - ), - EnumMember( - 'LIST_PARAGRAPH', -180, 'List Paragraph.' - ), - EnumMember( - 'MACRO_TEXT', -46, 'Macro Text.' - ), - EnumMember( - 'MESSAGE_HEADER', -74, 'Message Header.' - ), - EnumMember( - 'NAV_PANE', -90, 'Document Map.' - ), - EnumMember( - 'NORMAL', -1, 'Normal.' - ), - EnumMember( - 'NORMAL_INDENT', -29, 'Normal Indent.' - ), - EnumMember( - 'NORMAL_OBJECT', -158, 'Normal (applied to an object).' - ), - EnumMember( - 'NORMAL_TABLE', -106, 'Normal (applied within a table).' - ), - EnumMember( - 'NOTE_HEADING', -80, 'Note Heading.' - ), - EnumMember( - 'PAGE_NUMBER', -42, 'Page Number.' - ), - EnumMember( - 'PLAIN_TEXT', -91, 'Plain Text.' - ), - EnumMember( - 'QUOTE', -181, 'Quote.' - ), - EnumMember( - 'SALUTATION', -76, 'Salutation.' - ), - EnumMember( - 'SIGNATURE', -65, 'Signature.' - ), - EnumMember( - 'STRONG', -88, 'Strong.' - ), - EnumMember( - 'SUBTITLE', -75, 'Subtitle.' - ), - EnumMember( - 'SUBTLE_EMPHASIS', -261, 'Subtle Emphasis.' - ), - EnumMember( - 'SUBTLE_REFERENCE', -263, 'Subtle Reference.' - ), - EnumMember( - 'TABLE_COLORFUL_GRID', -172, 'Colorful Grid.' - ), - EnumMember( - 'TABLE_COLORFUL_LIST', -171, 'Colorful List.' - ), - EnumMember( - 'TABLE_COLORFUL_SHADING', -170, 'Colorful Shading.' - ), - EnumMember( - 'TABLE_DARK_LIST', -169, 'Dark List.' - ), - EnumMember( - 'TABLE_LIGHT_GRID', -161, 'Light Grid.' - ), - EnumMember( - 'TABLE_LIGHT_GRID_ACCENT_1', -175, 'Light Grid Accent 1.' - ), - EnumMember( - 'TABLE_LIGHT_LIST', -160, 'Light List.' - ), - EnumMember( - 'TABLE_LIGHT_LIST_ACCENT_1', -174, 'Light List Accent 1.' - ), - EnumMember( - 'TABLE_LIGHT_SHADING', -159, 'Light Shading.' - ), - EnumMember( - 'TABLE_LIGHT_SHADING_ACCENT_1', -173, 'Light Shading Accent 1.' - ), - EnumMember( - 'TABLE_MEDIUM_GRID_1', -166, 'Medium Grid 1.' - ), - EnumMember( - 'TABLE_MEDIUM_GRID_2', -167, 'Medium Grid 2.' - ), - EnumMember( - 'TABLE_MEDIUM_GRID_3', -168, 'Medium Grid 3.' - ), - EnumMember( - 'TABLE_MEDIUM_LIST_1', -164, 'Medium List 1.' - ), - EnumMember( - 'TABLE_MEDIUM_LIST_1_ACCENT_1', -178, 'Medium List 1 Accent 1.' - ), - EnumMember( - 'TABLE_MEDIUM_LIST_2', -165, 'Medium List 2.' - ), - EnumMember( - 'TABLE_MEDIUM_SHADING_1', -162, 'Medium Shading 1.' - ), - EnumMember( - 'TABLE_MEDIUM_SHADING_1_ACCENT_1', -176, - 'Medium Shading 1 Accent 1.' - ), - EnumMember( - 'TABLE_MEDIUM_SHADING_2', -163, 'Medium Shading 2.' - ), - EnumMember( - 'TABLE_MEDIUM_SHADING_2_ACCENT_1', -177, - 'Medium Shading 2 Accent 1.' - ), - EnumMember( - 'TABLE_OF_AUTHORITIES', -45, 'Table of Authorities.' - ), - EnumMember( - 'TABLE_OF_FIGURES', -36, 'Table of Figures.' - ), - EnumMember( - 'TITLE', -63, 'Title.' - ), - EnumMember( - 'TOAHEADING', -47, 'TOA Heading.' - ), - EnumMember( - 'TOC_1', -20, 'TOC 1.' - ), - EnumMember( - 'TOC_2', -21, 'TOC 2.' - ), - EnumMember( - 'TOC_3', -22, 'TOC 3.' - ), - EnumMember( - 'TOC_4', -23, 'TOC 4.' - ), - EnumMember( - 'TOC_5', -24, 'TOC 5.' - ), - EnumMember( - 'TOC_6', -25, 'TOC 6.' - ), - EnumMember( - 'TOC_7', -26, 'TOC 7.' - ), - EnumMember( - 'TOC_8', -27, 'TOC 8.' - ), - EnumMember( - 'TOC_9', -28, 'TOC 9.' - ), - ) - - -class WD_STYLE_TYPE(XmlEnumeration): - """ - Specifies one of the four style types: paragraph, character, list, or - table. - - Example:: - - from docx import Document - from docx.enum.style import WD_STYLE_TYPE - - styles = Document().styles - assert styles[0].type == WD_STYLE_TYPE.PARAGRAPH - """ - - __ms_name__ = 'WdStyleType' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff196870.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'CHARACTER', 2, 'character', 'Character style.' - ), - XmlMappedEnumMember( - 'LIST', 4, 'numbering', 'List style.' - ), - XmlMappedEnumMember( - 'PARAGRAPH', 1, 'paragraph', 'Paragraph style.' - ), - XmlMappedEnumMember( - 'TABLE', 3, 'table', 'Table style.' - ), - ) diff --git a/build/lib/docx/enum/table.py b/build/lib/docx/enum/table.py deleted file mode 100644 index eedab082e..000000000 --- a/build/lib/docx/enum/table.py +++ /dev/null @@ -1,146 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations related to tables in WordprocessingML files -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from .base import ( - alias, Enumeration, EnumMember, XmlEnumeration, XmlMappedEnumMember -) - - -@alias('WD_ALIGN_VERTICAL') -class WD_CELL_VERTICAL_ALIGNMENT(XmlEnumeration): - """ - alias: **WD_ALIGN_VERTICAL** - - Specifies the vertical alignment of text in one or more cells of a table. - - Example:: - - from docx.enum.table import WD_ALIGN_VERTICAL - - table = document.add_table(3, 3) - table.cell(0, 0).vertical_alignment = WD_ALIGN_VERTICAL.BOTTOM - """ - - __ms_name__ = 'WdCellVerticalAlignment' - - __url__ = 'https://msdn.microsoft.com/en-us/library/office/ff193345.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'TOP', 0, 'top', 'Text is aligned to the top border of the cell.' - ), - XmlMappedEnumMember( - 'CENTER', 1, 'center', 'Text is aligned to the center of the cel' - 'l.' - ), - XmlMappedEnumMember( - 'BOTTOM', 3, 'bottom', 'Text is aligned to the bottom border of ' - 'the cell.' - ), - XmlMappedEnumMember( - 'BOTH', 101, 'both', 'This is an option in the OpenXml spec, but' - ' not in Word itself. It\'s not clear what Word behavior this se' - 'tting produces. If you find out please let us know and we\'ll u' - 'pdate this documentation. Otherwise, probably best to avoid thi' - 's option.' - ), - ) - - -@alias('WD_ROW_HEIGHT') -class WD_ROW_HEIGHT_RULE(XmlEnumeration): - """ - alias: **WD_ROW_HEIGHT** - - Specifies the rule for determining the height of a table row - - Example:: - - from docx.enum.table import WD_ROW_HEIGHT_RULE - - table = document.add_table(3, 3) - table.rows[0].height_rule = WD_ROW_HEIGHT_RULE.EXACTLY - """ - - __ms_name__ = "WdRowHeightRule" - - __url__ = 'https://msdn.microsoft.com/en-us/library/office/ff193620.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'AUTO', 0, 'auto', 'The row height is adjusted to accommodate th' - 'e tallest value in the row.' - ), - XmlMappedEnumMember( - 'AT_LEAST', 1, 'atLeast', 'The row height is at least a minimum ' - 'specified value.' - ), - XmlMappedEnumMember( - 'EXACTLY', 2, 'exact', 'The row height is an exact value.' - ), - ) - - -class WD_TABLE_ALIGNMENT(XmlEnumeration): - """ - Specifies table justification type. - - Example:: - - from docx.enum.table import WD_TABLE_ALIGNMENT - - table = document.add_table(3, 3) - table.alignment = WD_TABLE_ALIGNMENT.CENTER - """ - - __ms_name__ = 'WdRowAlignment' - - __url__ = ' http://office.microsoft.com/en-us/word-help/HV080607259.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'LEFT', 0, 'left', 'Left-aligned' - ), - XmlMappedEnumMember( - 'CENTER', 1, 'center', 'Center-aligned.' - ), - XmlMappedEnumMember( - 'RIGHT', 2, 'right', 'Right-aligned.' - ), - ) - - -class WD_TABLE_DIRECTION(Enumeration): - """ - Specifies the direction in which an application orders cells in the - specified table or row. - - Example:: - - from docx.enum.table import WD_TABLE_DIRECTION - - table = document.add_table(3, 3) - table.direction = WD_TABLE_DIRECTION.RTL - """ - - __ms_name__ = 'WdTableDirection' - - __url__ = ' http://msdn.microsoft.com/en-us/library/ff835141.aspx' - - __members__ = ( - EnumMember( - 'LTR', 0, 'The table or row is arranged with the first column ' - 'in the leftmost position.' - ), - EnumMember( - 'RTL', 1, 'The table or row is arranged with the first column ' - 'in the rightmost position.' - ), - ) diff --git a/build/lib/docx/enum/text.py b/build/lib/docx/enum/text.py deleted file mode 100644 index f4111eb92..000000000 --- a/build/lib/docx/enum/text.py +++ /dev/null @@ -1,351 +0,0 @@ -# encoding: utf-8 - -""" -Enumerations related to text in WordprocessingML files -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from .base import alias, EnumMember, XmlEnumeration, XmlMappedEnumMember - - -@alias('WD_ALIGN_PARAGRAPH') -class WD_PARAGRAPH_ALIGNMENT(XmlEnumeration): - """ - alias: **WD_ALIGN_PARAGRAPH** - - Specifies paragraph justification type. - - Example:: - - from docx.enum.text import WD_ALIGN_PARAGRAPH - - paragraph = document.add_paragraph() - paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER - """ - - __ms_name__ = 'WdParagraphAlignment' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff835817.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'LEFT', 0, 'left', 'Left-aligned' - ), - XmlMappedEnumMember( - 'CENTER', 1, 'center', 'Center-aligned.' - ), - XmlMappedEnumMember( - 'RIGHT', 2, 'right', 'Right-aligned.' - ), - XmlMappedEnumMember( - 'JUSTIFY', 3, 'both', 'Fully justified.' - ), - XmlMappedEnumMember( - 'DISTRIBUTE', 4, 'distribute', 'Paragraph characters are distrib' - 'uted to fill the entire width of the paragraph.' - ), - XmlMappedEnumMember( - 'JUSTIFY_MED', 5, 'mediumKashida', 'Justified with a medium char' - 'acter compression ratio.' - ), - XmlMappedEnumMember( - 'JUSTIFY_HI', 7, 'highKashida', 'Justified with a high character' - ' compression ratio.' - ), - XmlMappedEnumMember( - 'JUSTIFY_LOW', 8, 'lowKashida', 'Justified with a low character ' - 'compression ratio.' - ), - XmlMappedEnumMember( - 'THAI_JUSTIFY', 9, 'thaiDistribute', 'Justified according to Tha' - 'i formatting layout.' - ), - ) - - -class WD_BREAK_TYPE(object): - """ - Corresponds to WdBreakType enumeration - http://msdn.microsoft.com/en-us/library/office/ff195905.aspx - """ - COLUMN = 8 - LINE = 6 - LINE_CLEAR_LEFT = 9 - LINE_CLEAR_RIGHT = 10 - LINE_CLEAR_ALL = 11 # added for consistency, not in MS version - PAGE = 7 - SECTION_CONTINUOUS = 3 - SECTION_EVEN_PAGE = 4 - SECTION_NEXT_PAGE = 2 - SECTION_ODD_PAGE = 5 - TEXT_WRAPPING = 11 - -WD_BREAK = WD_BREAK_TYPE - - -@alias('WD_COLOR') -class WD_COLOR_INDEX(XmlEnumeration): - """ - Specifies a standard preset color to apply. Used for font highlighting and - perhaps other applications. - """ - - __ms_name__ = 'WdColorIndex' - - __url__ = 'https://msdn.microsoft.com/EN-US/library/office/ff195343.aspx' - - __members__ = ( - XmlMappedEnumMember( - None, None, None, 'Color is inherited from the style hierarchy.' - ), - XmlMappedEnumMember( - 'AUTO', 0, 'default', 'Automatic color. Default; usually black.' - ), - XmlMappedEnumMember( - 'BLACK', 1, 'black', 'Black color.' - ), - XmlMappedEnumMember( - 'BLUE', 2, 'blue', 'Blue color' - ), - XmlMappedEnumMember( - 'BRIGHT_GREEN', 4, 'green', 'Bright green color.' - ), - XmlMappedEnumMember( - 'DARK_BLUE', 9, 'darkBlue', 'Dark blue color.' - ), - XmlMappedEnumMember( - 'DARK_RED', 13, 'darkRed', 'Dark red color.' - ), - XmlMappedEnumMember( - 'DARK_YELLOW', 14, 'darkYellow', 'Dark yellow color.' - ), - XmlMappedEnumMember( - 'GRAY_25', 16, 'lightGray', '25% shade of gray color.' - ), - XmlMappedEnumMember( - 'GRAY_50', 15, 'darkGray', '50% shade of gray color.' - ), - XmlMappedEnumMember( - 'GREEN', 11, 'darkGreen', 'Green color.' - ), - XmlMappedEnumMember( - 'PINK', 5, 'magenta', 'Pink color.' - ), - XmlMappedEnumMember( - 'RED', 6, 'red', 'Red color.' - ), - XmlMappedEnumMember( - 'TEAL', 10, 'darkCyan', 'Teal color.' - ), - XmlMappedEnumMember( - 'TURQUOISE', 3, 'cyan', 'Turquoise color.' - ), - XmlMappedEnumMember( - 'VIOLET', 12, 'darkMagenta', 'Violet color.' - ), - XmlMappedEnumMember( - 'WHITE', 8, 'white', 'White color.' - ), - XmlMappedEnumMember( - 'YELLOW', 7, 'yellow', 'Yellow color.' - ), - ) - - -class WD_LINE_SPACING(XmlEnumeration): - """ - Specifies a line spacing format to be applied to a paragraph. - - Example:: - - from docx.enum.text import WD_LINE_SPACING - - paragraph = document.add_paragraph() - paragraph.line_spacing_rule = WD_LINE_SPACING.EXACTLY - """ - - __ms_name__ = 'WdLineSpacing' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff844910.aspx' - - __members__ = ( - EnumMember( - 'ONE_POINT_FIVE', 1, 'Space-and-a-half line spacing.' - ), - XmlMappedEnumMember( - 'AT_LEAST', 3, 'atLeast', 'Line spacing is always at least the s' - 'pecified amount. The amount is specified separately.' - ), - EnumMember( - 'DOUBLE', 2, 'Double spaced.' - ), - XmlMappedEnumMember( - 'EXACTLY', 4, 'exact', 'Line spacing is exactly the specified am' - 'ount. The amount is specified separately.' - ), - XmlMappedEnumMember( - 'MULTIPLE', 5, 'auto', 'Line spacing is specified as a multiple ' - 'of line heights. Changing the font size will change the line sp' - 'acing proportionately.' - ), - EnumMember( - 'SINGLE', 0, 'Single spaced (default).' - ), - ) - - -class WD_TAB_ALIGNMENT(XmlEnumeration): - """ - Specifies the tab stop alignment to apply. - """ - - __ms_name__ = 'WdTabAlignment' - - __url__ = 'https://msdn.microsoft.com/EN-US/library/office/ff195609.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'LEFT', 0, 'left', 'Left-aligned.' - ), - XmlMappedEnumMember( - 'CENTER', 1, 'center', 'Center-aligned.' - ), - XmlMappedEnumMember( - 'RIGHT', 2, 'right', 'Right-aligned.' - ), - XmlMappedEnumMember( - 'DECIMAL', 3, 'decimal', 'Decimal-aligned.' - ), - XmlMappedEnumMember( - 'BAR', 4, 'bar', 'Bar-aligned.' - ), - XmlMappedEnumMember( - 'LIST', 6, 'list', 'List-aligned. (deprecated)' - ), - XmlMappedEnumMember( - 'CLEAR', 101, 'clear', 'Clear an inherited tab stop.' - ), - XmlMappedEnumMember( - 'END', 102, 'end', 'Right-aligned. (deprecated)' - ), - XmlMappedEnumMember( - 'NUM', 103, 'num', 'Left-aligned. (deprecated)' - ), - XmlMappedEnumMember( - 'START', 104, 'start', 'Left-aligned. (deprecated)' - ), - ) - - -class WD_TAB_LEADER(XmlEnumeration): - """ - Specifies the character to use as the leader with formatted tabs. - """ - - __ms_name__ = 'WdTabLeader' - - __url__ = 'https://msdn.microsoft.com/en-us/library/office/ff845050.aspx' - - __members__ = ( - XmlMappedEnumMember( - 'SPACES', 0, 'none', 'Spaces. Default.' - ), - XmlMappedEnumMember( - 'DOTS', 1, 'dot', 'Dots.' - ), - XmlMappedEnumMember( - 'DASHES', 2, 'hyphen', 'Dashes.' - ), - XmlMappedEnumMember( - 'LINES', 3, 'underscore', 'Double lines.' - ), - XmlMappedEnumMember( - 'HEAVY', 4, 'heavy', 'A heavy line.' - ), - XmlMappedEnumMember( - 'MIDDLE_DOT', 5, 'middleDot', 'A vertically-centered dot.' - ), - ) - - -class WD_UNDERLINE(XmlEnumeration): - """ - Specifies the style of underline applied to a run of characters. - """ - - __ms_name__ = 'WdUnderline' - - __url__ = 'http://msdn.microsoft.com/en-us/library/office/ff822388.aspx' - - __members__ = ( - XmlMappedEnumMember( - None, None, None, 'Inherit underline setting from containing par' - 'agraph.' - ), - XmlMappedEnumMember( - 'NONE', 0, 'none', 'No underline. This setting overrides any inh' - 'erited underline value, so can be used to remove underline from' - ' a run that inherits underlining from its containing paragraph.' - ' Note this is not the same as assigning |None| to Run.underline' - '. |None| is a valid assignment value, but causes the run to inh' - 'erit its underline value. Assigning ``WD_UNDERLINE.NONE`` cause' - 's underlining to be unconditionally turned off.' - ), - XmlMappedEnumMember( - 'SINGLE', 1, 'single', 'A single line. Note that this setting is' - 'write-only in the sense that |True| (rather than ``WD_UNDERLINE' - '.SINGLE``) is returned for a run having this setting.' - ), - XmlMappedEnumMember( - 'WORDS', 2, 'words', 'Underline individual words only.' - ), - XmlMappedEnumMember( - 'DOUBLE', 3, 'double', 'A double line.' - ), - XmlMappedEnumMember( - 'DOTTED', 4, 'dotted', 'Dots.' - ), - XmlMappedEnumMember( - 'THICK', 6, 'thick', 'A single thick line.' - ), - XmlMappedEnumMember( - 'DASH', 7, 'dash', 'Dashes.' - ), - XmlMappedEnumMember( - 'DOT_DASH', 9, 'dotDash', 'Alternating dots and dashes.' - ), - XmlMappedEnumMember( - 'DOT_DOT_DASH', 10, 'dotDotDash', 'An alternating dot-dot-dash p' - 'attern.' - ), - XmlMappedEnumMember( - 'WAVY', 11, 'wave', 'A single wavy line.' - ), - XmlMappedEnumMember( - 'DOTTED_HEAVY', 20, 'dottedHeavy', 'Heavy dots.' - ), - XmlMappedEnumMember( - 'DASH_HEAVY', 23, 'dashedHeavy', 'Heavy dashes.' - ), - XmlMappedEnumMember( - 'DOT_DASH_HEAVY', 25, 'dashDotHeavy', 'Alternating heavy dots an' - 'd heavy dashes.' - ), - XmlMappedEnumMember( - 'DOT_DOT_DASH_HEAVY', 26, 'dashDotDotHeavy', 'An alternating hea' - 'vy dot-dot-dash pattern.' - ), - XmlMappedEnumMember( - 'WAVY_HEAVY', 27, 'wavyHeavy', 'A heavy wavy line.' - ), - XmlMappedEnumMember( - 'DASH_LONG', 39, 'dashLong', 'Long dashes.' - ), - XmlMappedEnumMember( - 'WAVY_DOUBLE', 43, 'wavyDouble', 'A double wavy line.' - ), - XmlMappedEnumMember( - 'DASH_LONG_HEAVY', 55, 'dashLongHeavy', 'Long heavy dashes.' - ), - ) diff --git a/build/lib/docx/exceptions.py b/build/lib/docx/exceptions.py deleted file mode 100644 index 7a8b99c81..000000000 --- a/build/lib/docx/exceptions.py +++ /dev/null @@ -1,27 +0,0 @@ -# encoding: utf-8 - -""" -Exceptions used with python-docx. - -The base exception class is PythonDocxError. -""" - - -class PythonDocxError(Exception): - """ - Generic error class. - """ - - -class InvalidSpanError(PythonDocxError): - """ - Raised when an invalid merge region is specified in a request to merge - table cells. - """ - - -class InvalidXmlError(PythonDocxError): - """ - Raised when invalid XML is encountered, such as on attempt to access a - missing required child element - """ diff --git a/build/lib/docx/image/__init__.py b/build/lib/docx/image/__init__.py deleted file mode 100644 index 8ab3ada68..000000000 --- a/build/lib/docx/image/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# encoding: utf-8 - -""" -Provides objects that can characterize image streams as to content type and -size, as a required step in including them in a document. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from docx.image.bmp import Bmp -from docx.image.gif import Gif -from docx.image.jpeg import Exif, Jfif -from docx.image.png import Png -from docx.image.tiff import Tiff - - -SIGNATURES = ( - # class, offset, signature_bytes - (Png, 0, b'\x89PNG\x0D\x0A\x1A\x0A'), - (Jfif, 6, b'JFIF'), - (Exif, 6, b'Exif'), - (Gif, 0, b'GIF87a'), - (Gif, 0, b'GIF89a'), - (Tiff, 0, b'MM\x00*'), # big-endian (Motorola) TIFF - (Tiff, 0, b'II*\x00'), # little-endian (Intel) TIFF - (Bmp, 0, b'BM'), -) diff --git a/build/lib/docx/image/bmp.py b/build/lib/docx/image/bmp.py deleted file mode 100644 index d22f25871..000000000 --- a/build/lib/docx/image/bmp.py +++ /dev/null @@ -1,56 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import, division, print_function - -from .constants import MIME_TYPE -from .helpers import LITTLE_ENDIAN, StreamReader -from .image import BaseImageHeader - - -class Bmp(BaseImageHeader): - """ - Image header parser for BMP images - """ - @classmethod - def from_stream(cls, stream): - """ - Return |Bmp| instance having header properties parsed from the BMP - image in *stream*. - """ - stream_rdr = StreamReader(stream, LITTLE_ENDIAN) - - px_width = stream_rdr.read_long(0x12) - px_height = stream_rdr.read_long(0x16) - - horz_px_per_meter = stream_rdr.read_long(0x26) - vert_px_per_meter = stream_rdr.read_long(0x2A) - - horz_dpi = cls._dpi(horz_px_per_meter) - vert_dpi = cls._dpi(vert_px_per_meter) - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - @property - def content_type(self): - """ - MIME content type for this image, unconditionally `image/bmp` for - BMP images. - """ - return MIME_TYPE.BMP - - @property - def default_ext(self): - """ - Default filename extension, always 'bmp' for BMP images. - """ - return 'bmp' - - @staticmethod - def _dpi(px_per_meter): - """ - Return the integer pixels per inch from *px_per_meter*, defaulting to - 96 if *px_per_meter* is zero. - """ - if px_per_meter == 0: - return 96 - return int(round(px_per_meter * 0.0254)) diff --git a/build/lib/docx/image/constants.py b/build/lib/docx/image/constants.py deleted file mode 100644 index 90b469705..000000000 --- a/build/lib/docx/image/constants.py +++ /dev/null @@ -1,169 +0,0 @@ -# encoding: utf-8 - -""" -Constants specific the the image sub-package -""" - - -class JPEG_MARKER_CODE(object): - """ - JPEG marker codes - """ - TEM = b'\x01' - DHT = b'\xC4' - DAC = b'\xCC' - JPG = b'\xC8' - - SOF0 = b'\xC0' - SOF1 = b'\xC1' - SOF2 = b'\xC2' - SOF3 = b'\xC3' - SOF5 = b'\xC5' - SOF6 = b'\xC6' - SOF7 = b'\xC7' - SOF9 = b'\xC9' - SOFA = b'\xCA' - SOFB = b'\xCB' - SOFD = b'\xCD' - SOFE = b'\xCE' - SOFF = b'\xCF' - - RST0 = b'\xD0' - RST1 = b'\xD1' - RST2 = b'\xD2' - RST3 = b'\xD3' - RST4 = b'\xD4' - RST5 = b'\xD5' - RST6 = b'\xD6' - RST7 = b'\xD7' - - SOI = b'\xD8' - EOI = b'\xD9' - SOS = b'\xDA' - DQT = b'\xDB' # Define Quantization Table(s) - DNL = b'\xDC' - DRI = b'\xDD' - DHP = b'\xDE' - EXP = b'\xDF' - - APP0 = b'\xE0' - APP1 = b'\xE1' - APP2 = b'\xE2' - APP3 = b'\xE3' - APP4 = b'\xE4' - APP5 = b'\xE5' - APP6 = b'\xE6' - APP7 = b'\xE7' - APP8 = b'\xE8' - APP9 = b'\xE9' - APPA = b'\xEA' - APPB = b'\xEB' - APPC = b'\xEC' - APPD = b'\xED' - APPE = b'\xEE' - APPF = b'\xEF' - - STANDALONE_MARKERS = ( - TEM, SOI, EOI, RST0, RST1, RST2, RST3, RST4, RST5, RST6, RST7 - ) - - SOF_MARKER_CODES = ( - SOF0, SOF1, SOF2, SOF3, SOF5, SOF6, SOF7, SOF9, SOFA, SOFB, SOFD, - SOFE, SOFF - ) - - marker_names = { - b'\x00': 'UNKNOWN', - b'\xC0': 'SOF0', - b'\xC2': 'SOF2', - b'\xC4': 'DHT', - b'\xDA': 'SOS', # start of scan - b'\xD8': 'SOI', # start of image - b'\xD9': 'EOI', # end of image - b'\xDB': 'DQT', - b'\xE0': 'APP0', - b'\xE1': 'APP1', - b'\xE2': 'APP2', - b'\xED': 'APP13', - b'\xEE': 'APP14', - } - - @classmethod - def is_standalone(cls, marker_code): - return marker_code in cls.STANDALONE_MARKERS - - -class MIME_TYPE(object): - """ - Image content types - """ - BMP = 'image/bmp' - GIF = 'image/gif' - JPEG = 'image/jpeg' - PNG = 'image/png' - TIFF = 'image/tiff' - - -class PNG_CHUNK_TYPE(object): - """ - PNG chunk type names - """ - IHDR = 'IHDR' - pHYs = 'pHYs' - IEND = 'IEND' - - -class TIFF_FLD_TYPE(object): - """ - Tag codes for TIFF Image File Directory (IFD) entries. - """ - BYTE = 1 - ASCII = 2 - SHORT = 3 - LONG = 4 - RATIONAL = 5 - - field_type_names = { - 1: 'BYTE', 2: 'ASCII char', 3: 'SHORT', 4: 'LONG', - 5: 'RATIONAL' - } - - -TIFF_FLD = TIFF_FLD_TYPE - - -class TIFF_TAG(object): - """ - Tag codes for TIFF Image File Directory (IFD) entries. - """ - IMAGE_WIDTH = 0x0100 - IMAGE_LENGTH = 0x0101 - X_RESOLUTION = 0x011A - Y_RESOLUTION = 0x011B - RESOLUTION_UNIT = 0x0128 - - tag_names = { - 0x00FE: 'NewSubfileType', - 0x0100: 'ImageWidth', - 0x0101: 'ImageLength', - 0x0102: 'BitsPerSample', - 0x0103: 'Compression', - 0x0106: 'PhotometricInterpretation', - 0x010E: 'ImageDescription', - 0x010F: 'Make', - 0x0110: 'Model', - 0x0111: 'StripOffsets', - 0x0112: 'Orientation', - 0x0115: 'SamplesPerPixel', - 0x0117: 'StripByteCounts', - 0x011A: 'XResolution', - 0x011B: 'YResolution', - 0x011C: 'PlanarConfiguration', - 0x0128: 'ResolutionUnit', - 0x0131: 'Software', - 0x0132: 'DateTime', - 0x0213: 'YCbCrPositioning', - 0x8769: 'ExifTag', - 0x8825: 'GPS IFD', - 0xC4A5: 'PrintImageMatching', - } diff --git a/build/lib/docx/image/emf.py b/build/lib/docx/image/emf.py deleted file mode 100644 index e2701c04f..000000000 --- a/build/lib/docx/image/emf.py +++ /dev/null @@ -1,70 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import, division, print_function - -from .constants import MIME_TYPE -from .exceptions import InvalidImageStreamError -from .helpers import BIG_ENDIAN, StreamReader -from .image import BaseImageHeader -import struct - -class Emf(BaseImageHeader): - """ - Image header parser for PNG images - """ - @property - def content_type(self): - """ - MIME content type for this image, unconditionally `image/png` for - PNG images. - """ - return MIME_TYPE.EMF - - @property - def default_ext(self): - """ - Default filename extension, always 'png' for PNG images. - """ - return 'emf' - - @classmethod - def from_stream(cls, stream,filename=None): - """ - Return a |Emf| instance having header properties parsed from image in - *stream*. - """ - - """ - @0 DWORD iType; // fixed - @4 DWORD nSize; // var - @8 RECTL rclBounds; - @24 RECTL rclFrame; // .01 millimeter units L T R B - @40 DWORD dSignature; // ENHMETA_SIGNATURE = 0x464D4520 - DWORD nVersion; - DWORD nBytes; - DWORD nRecords; - WORD nHandles; - WORD sReserved; - DWORD nDescription; - DWORD offDescription; - DWORD nPalEntries; - SIZEL szlDevice; - SIZEL szlMillimeters; - """ - stream.seek(0) - x = stream.read(40) - stream.seek(0) - iType,nSize = struct.unpack("ii",x[0:8]) - rclBounds = struct.unpack("iiii",x[8:24]) - rclFrame = struct.unpack("iiii",x[24:40]) - - dpi = 300 - horz_dpi = dpi - vert_dpi = dpi - mmwidth = (rclFrame[2]-rclFrame[0])/100.0 - mmheight = (rclFrame[3]-rclFrame[1])/100.0 - px_width = int(mmwidth*dpi*0.03937008) - px_height = int(mmheight*dpi*0.03937008) - - #1 dot/inch = 0.03937008 pixel/millimeter - return cls(px_width,px_height,horz_dpi,vert_dpi) \ No newline at end of file diff --git a/build/lib/docx/image/exceptions.py b/build/lib/docx/image/exceptions.py deleted file mode 100644 index f233edc4e..000000000 --- a/build/lib/docx/image/exceptions.py +++ /dev/null @@ -1,23 +0,0 @@ -# encoding: utf-8 - -""" -Exceptions specific the the image sub-package -""" - - -class InvalidImageStreamError(Exception): - """ - The recognized image stream appears to be corrupted - """ - - -class UnexpectedEndOfFileError(Exception): - """ - EOF was unexpectedly encountered while reading an image stream. - """ - - -class UnrecognizedImageError(Exception): - """ - The provided image stream could not be recognized. - """ diff --git a/build/lib/docx/image/gif.py b/build/lib/docx/image/gif.py deleted file mode 100644 index 57f037d80..000000000 --- a/build/lib/docx/image/gif.py +++ /dev/null @@ -1,47 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import, division, print_function - -from struct import Struct - -from .constants import MIME_TYPE -from .image import BaseImageHeader - - -class Gif(BaseImageHeader): - """ - Image header parser for GIF images. Note that the GIF format does not - support resolution (DPI) information. Both horizontal and vertical DPI - default to 72. - """ - @classmethod - def from_stream(cls, stream): - """ - Return |Gif| instance having header properties parsed from GIF image - in *stream*. - """ - px_width, px_height = cls._dimensions_from_stream(stream) - return cls(px_width, px_height, 72, 72) - - @property - def content_type(self): - """ - MIME content type for this image, unconditionally `image/gif` for - GIF images. - """ - return MIME_TYPE.GIF - - @property - def default_ext(self): - """ - Default filename extension, always 'gif' for GIF images. - """ - return 'gif' - - @classmethod - def _dimensions_from_stream(cls, stream): - stream.seek(6) - bytes_ = stream.read(4) - struct = Struct('L' - return self._read_int(fmt, base, offset) - - def read_short(self, base, offset=0): - """ - Return the int value of the two bytes at the file position determined - by *base* and *offset*, similarly to ``read_long()`` above. - """ - fmt = b'H' - return self._read_int(fmt, base, offset) - - def read_str(self, char_count, base, offset=0): - """ - Return a string containing the *char_count* bytes at the file - position determined by self._base_offset + *base* + *offset*. - """ - def str_struct(char_count): - format_ = '%ds' % char_count - return Struct(format_) - struct = str_struct(char_count) - chars = self._unpack_item(struct, base, offset) - unicode_str = chars.decode('UTF-8') - return unicode_str - - def seek(self, base, offset=0): - location = self._base_offset + base + offset - self._stream.seek(location) - - def tell(self): - """ - Allow pass-through tell() call - """ - return self._stream.tell() - - def _read_bytes(self, byte_count, base, offset): - self.seek(base, offset) - bytes_ = self._stream.read(byte_count) - if len(bytes_) < byte_count: - raise UnexpectedEndOfFileError - return bytes_ - - def _read_int(self, fmt, base, offset): - struct = Struct(fmt) - return self._unpack_item(struct, base, offset) - - def _unpack_item(self, struct, base, offset): - bytes_ = self._read_bytes(struct.size, base, offset) - return struct.unpack(bytes_)[0] diff --git a/build/lib/docx/image/image.py b/build/lib/docx/image/image.py deleted file mode 100644 index 3df31672f..000000000 --- a/build/lib/docx/image/image.py +++ /dev/null @@ -1,263 +0,0 @@ -# encoding: utf-8 - -""" -Provides objects that can characterize image streams as to content type and -size, as a required step in including them in a document. -""" - -from __future__ import absolute_import, division, print_function - -import hashlib -import os - -from ..compat import BytesIO, is_string -from .exceptions import UnrecognizedImageError -from ..shared import Emu, Inches, lazyproperty - - -class Image(object): - """ - Graphical image stream such as JPEG, PNG, or GIF with properties and - methods required by ImagePart. - """ - def __init__(self, blob, filename, image_header): - super(Image, self).__init__() - self._blob = blob - self._filename = filename - self._image_header = image_header - - @classmethod - def from_blob(cls, blob): - """ - Return a new |Image| subclass instance parsed from the image binary - contained in *blob*. - """ - stream = BytesIO(blob) - return cls._from_stream(stream, blob) - - @classmethod - def from_file(cls, image_descriptor): - """ - Return a new |Image| subclass instance loaded from the image file - identified by *image_descriptor*, a path or file-like object. - """ - if is_string(image_descriptor): - path = image_descriptor - with open(path, 'rb') as f: - blob = f.read() - stream = BytesIO(blob) - filename = os.path.basename(path) - else: - stream = image_descriptor - stream.seek(0) - blob = stream.read() - filename = None - return cls._from_stream(stream, blob, filename) - - @property - def blob(self): - """ - The bytes of the image 'file' - """ - return self._blob - - @property - def content_type(self): - """ - MIME content type for this image, e.g. ``'image/jpeg'`` for a JPEG - image - """ - return self._image_header.content_type - - @lazyproperty - def ext(self): - """ - The file extension for the image. If an actual one is available from - a load filename it is used. Otherwise a canonical extension is - assigned based on the content type. Does not contain the leading - period, e.g. 'jpg', not '.jpg'. - """ - return os.path.splitext(self._filename)[1][1:] - - @property - def filename(self): - """ - Original image file name, if loaded from disk, or a generic filename - if loaded from an anonymous stream. - """ - return self._filename - - @property - def px_width(self): - """ - The horizontal pixel dimension of the image - """ - return self._image_header.px_width - - @property - def px_height(self): - """ - The vertical pixel dimension of the image - """ - return self._image_header.px_height - - @property - def horz_dpi(self): - """ - Integer dots per inch for the width of this image. Defaults to 72 - when not present in the file, as is often the case. - """ - return self._image_header.horz_dpi - - @property - def vert_dpi(self): - """ - Integer dots per inch for the height of this image. Defaults to 72 - when not present in the file, as is often the case. - """ - return self._image_header.vert_dpi - - @property - def width(self): - """ - A |Length| value representing the native width of the image, - calculated from the values of `px_width` and `horz_dpi`. - """ - return Inches(self.px_width / self.horz_dpi) - - @property - def height(self): - """ - A |Length| value representing the native height of the image, - calculated from the values of `px_height` and `vert_dpi`. - """ - return Inches(self.px_height / self.vert_dpi) - - def scaled_dimensions(self, width=None, height=None): - """ - Return a (cx, cy) 2-tuple representing the native dimensions of this - image scaled by applying the following rules to *width* and *height*. - If both *width* and *height* are specified, the return value is - (*width*, *height*); no scaling is performed. If only one is - specified, it is used to compute a scaling factor that is then - applied to the unspecified dimension, preserving the aspect ratio of - the image. If both *width* and *height* are |None|, the native - dimensions are returned. The native dimensions are calculated using - the dots-per-inch (dpi) value embedded in the image, defaulting to 72 - dpi if no value is specified, as is often the case. The returned - values are both |Length| objects. - """ - if width is None and height is None: - return self.width, self.height - - if width is None: - scaling_factor = float(height) / float(self.height) - width = round(self.width * scaling_factor) - - if height is None: - scaling_factor = float(width) / float(self.width) - height = round(self.height * scaling_factor) - - return Emu(width), Emu(height) - - @lazyproperty - def sha1(self): - """ - SHA1 hash digest of the image blob - """ - return hashlib.sha1(self._blob).hexdigest() - - @classmethod - def _from_stream(cls, stream, blob, filename=None): - """ - Return an instance of the |Image| subclass corresponding to the - format of the image in *stream*. - """ - image_header = _ImageHeaderFactory(stream) - if filename is None: - filename = 'image.%s' % image_header.default_ext - return cls(blob, filename, image_header) - - -def _ImageHeaderFactory(stream): - """ - Return a |BaseImageHeader| subclass instance that knows how to parse the - headers of the image in *stream*. - """ - from docx.image import SIGNATURES - - def read_32(stream): - stream.seek(0) - return stream.read(64) - - header = read_32(stream) - for cls, offset, signature_bytes in SIGNATURES: - end = offset + len(signature_bytes) - found_bytes = header[offset:end] - if found_bytes == signature_bytes: - return cls.from_stream(stream) - raise UnrecognizedImageError - - -class BaseImageHeader(object): - """ - Base class for image header subclasses like |Jpeg| and |Tiff|. - """ - def __init__(self, px_width, px_height, horz_dpi, vert_dpi): - self._px_width = px_width - self._px_height = px_height - self._horz_dpi = horz_dpi - self._vert_dpi = vert_dpi - - @property - def content_type(self): - """ - Abstract property definition, must be implemented by all subclasses. - """ - msg = ( - 'content_type property must be implemented by all subclasses of ' - 'BaseImageHeader' - ) - raise NotImplementedError(msg) - - @property - def default_ext(self): - """ - Default filename extension for images of this type. An abstract - property definition, must be implemented by all subclasses. - """ - msg = ( - 'default_ext property must be implemented by all subclasses of ' - 'BaseImageHeader' - ) - raise NotImplementedError(msg) - - @property - def px_width(self): - """ - The horizontal pixel dimension of the image - """ - return self._px_width - - @property - def px_height(self): - """ - The vertical pixel dimension of the image - """ - return self._px_height - - @property - def horz_dpi(self): - """ - Integer dots per inch for the width of this image. Defaults to 72 - when not present in the file, as is often the case. - """ - return self._horz_dpi - - @property - def vert_dpi(self): - """ - Integer dots per inch for the height of this image. Defaults to 72 - when not present in the file, as is often the case. - """ - return self._vert_dpi diff --git a/build/lib/docx/image/jpeg.py b/build/lib/docx/image/jpeg.py deleted file mode 100644 index 8a263b6c5..000000000 --- a/build/lib/docx/image/jpeg.py +++ /dev/null @@ -1,498 +0,0 @@ -# encoding: utf-8 - -""" -Objects related to parsing headers of JPEG image streams, both JFIF and Exif -sub-formats. -""" - -from __future__ import absolute_import, division, print_function - -from ..compat import BytesIO -from .constants import JPEG_MARKER_CODE, MIME_TYPE -from .helpers import BIG_ENDIAN, StreamReader -from .image import BaseImageHeader -from .tiff import Tiff - - -class Jpeg(BaseImageHeader): - """ - Base class for JFIF and EXIF subclasses. - """ - @property - def content_type(self): - """ - MIME content type for this image, unconditionally `image/jpeg` for - JPEG images. - """ - return MIME_TYPE.JPEG - - @property - def default_ext(self): - """ - Default filename extension, always 'jpg' for JPG images. - """ - return 'jpg' - - -class Exif(Jpeg): - """ - Image header parser for Exif image format - """ - @classmethod - def from_stream(cls, stream): - """ - Return |Exif| instance having header properties parsed from Exif - image in *stream*. - """ - markers = _JfifMarkers.from_stream(stream) - # print('\n%s' % markers) - - px_width = markers.sof.px_width - px_height = markers.sof.px_height - horz_dpi = markers.app1.horz_dpi - vert_dpi = markers.app1.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class Jfif(Jpeg): - """ - Image header parser for JFIF image format - """ - @classmethod - def from_stream(cls, stream): - """ - Return a |Jfif| instance having header properties parsed from image - in *stream*. - """ - markers = _JfifMarkers.from_stream(stream) - - px_width = markers.sof.px_width - px_height = markers.sof.px_height - horz_dpi = markers.app0.horz_dpi - vert_dpi = markers.app0.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class _JfifMarkers(object): - """ - Sequence of markers in a JPEG file, perhaps truncated at first SOS marker - for performance reasons. - """ - def __init__(self, markers): - super(_JfifMarkers, self).__init__() - self._markers = list(markers) - - def __str__(self): # pragma: no cover - """ - Returns a tabular listing of the markers in this instance, which can - be handy for debugging and perhaps other uses. - """ - header = ' offset seglen mc name\n======= ====== == =====' - tmpl = '%7d %6d %02X %s' - rows = [] - for marker in self._markers: - rows.append(tmpl % ( - marker.offset, marker.segment_length, - ord(marker.marker_code), marker.name - )) - lines = [header] + rows - return '\n'.join(lines) - - @classmethod - def from_stream(cls, stream): - """ - Return a |_JfifMarkers| instance containing a |_JfifMarker| subclass - instance for each marker in *stream*. - """ - marker_parser = _MarkerParser.from_stream(stream) - markers = [] - for marker in marker_parser.iter_markers(): - markers.append(marker) - if marker.marker_code == JPEG_MARKER_CODE.SOS: - break - return cls(markers) - - @property - def app0(self): - """ - First APP0 marker in image markers. - """ - for m in self._markers: - if m.marker_code == JPEG_MARKER_CODE.APP0: - return m - raise KeyError('no APP0 marker in image') - - @property - def app1(self): - """ - First APP1 marker in image markers. - """ - for m in self._markers: - if m.marker_code == JPEG_MARKER_CODE.APP1: - return m - raise KeyError('no APP1 marker in image') - - @property - def sof(self): - """ - First start of frame (SOFn) marker in this sequence. - """ - for m in self._markers: - if m.marker_code in JPEG_MARKER_CODE.SOF_MARKER_CODES: - return m - raise KeyError('no start of frame (SOFn) marker in image') - - -class _MarkerParser(object): - """ - Service class that knows how to parse a JFIF stream and iterate over its - markers. - """ - def __init__(self, stream_reader): - super(_MarkerParser, self).__init__() - self._stream = stream_reader - - @classmethod - def from_stream(cls, stream): - """ - Return a |_MarkerParser| instance to parse JFIF markers from - *stream*. - """ - stream_reader = StreamReader(stream, BIG_ENDIAN) - return cls(stream_reader) - - def iter_markers(self): - """ - Generate a (marker_code, segment_offset) 2-tuple for each marker in - the JPEG *stream*, in the order they occur in the stream. - """ - marker_finder = _MarkerFinder.from_stream(self._stream) - start = 0 - marker_code = None - while marker_code != JPEG_MARKER_CODE.EOI: - marker_code, segment_offset = marker_finder.next(start) - marker = _MarkerFactory( - marker_code, self._stream, segment_offset - ) - yield marker - start = segment_offset + marker.segment_length - - -class _MarkerFinder(object): - """ - Service class that knows how to find the next JFIF marker in a stream. - """ - def __init__(self, stream): - super(_MarkerFinder, self).__init__() - self._stream = stream - - @classmethod - def from_stream(cls, stream): - """ - Return a |_MarkerFinder| instance to find JFIF markers in *stream*. - """ - return cls(stream) - - def next(self, start): - """ - Return a (marker_code, segment_offset) 2-tuple identifying and - locating the first marker in *stream* occuring after offset *start*. - The returned *segment_offset* points to the position immediately - following the 2-byte marker code, the start of the marker segment, - for those markers that have a segment. - """ - position = start - while True: - # skip over any non-\xFF bytes - position = self._offset_of_next_ff_byte(start=position) - # skip over any \xFF padding bytes - position, byte_ = self._next_non_ff_byte(start=position+1) - # 'FF 00' sequence is not a marker, start over if found - if byte_ == b'\x00': - continue - # this is a marker, gather return values and break out of scan - marker_code, segment_offset = byte_, position+1 - break - return marker_code, segment_offset - - def _next_non_ff_byte(self, start): - """ - Return an offset, byte 2-tuple for the next byte in *stream* that is - not '\xFF', starting with the byte at offset *start*. If the byte at - offset *start* is not '\xFF', *start* and the returned *offset* will - be the same. - """ - self._stream.seek(start) - byte_ = self._read_byte() - while byte_ == b'\xFF': - byte_ = self._read_byte() - offset_of_non_ff_byte = self._stream.tell() - 1 - return offset_of_non_ff_byte, byte_ - - def _offset_of_next_ff_byte(self, start): - """ - Return the offset of the next '\xFF' byte in *stream* starting with - the byte at offset *start*. Returns *start* if the byte at that - offset is a hex 255; it does not necessarily advance in the stream. - """ - self._stream.seek(start) - byte_ = self._read_byte() - while byte_ != b'\xFF': - byte_ = self._read_byte() - offset_of_ff_byte = self._stream.tell() - 1 - return offset_of_ff_byte - - def _read_byte(self): - """ - Return the next byte read from stream. Raise Exception if stream is - at end of file. - """ - byte_ = self._stream.read(1) - if not byte_: # pragma: no cover - raise Exception('unexpected end of file') - return byte_ - - -def _MarkerFactory(marker_code, stream, offset): - """ - Return |_Marker| or subclass instance appropriate for marker at *offset* - in *stream* having *marker_code*. - """ - if marker_code == JPEG_MARKER_CODE.APP0: - marker_cls = _App0Marker - elif marker_code == JPEG_MARKER_CODE.APP1: - marker_cls = _App1Marker - elif marker_code in JPEG_MARKER_CODE.SOF_MARKER_CODES: - marker_cls = _SofMarker - else: - marker_cls = _Marker - return marker_cls.from_stream(stream, marker_code, offset) - - -class _Marker(object): - """ - Base class for JFIF marker classes. Represents a marker and its segment - occuring in a JPEG byte stream. - """ - def __init__(self, marker_code, offset, segment_length): - super(_Marker, self).__init__() - self._marker_code = marker_code - self._offset = offset - self._segment_length = segment_length - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """ - Return a generic |_Marker| instance for the marker at *offset* in - *stream* having *marker_code*. - """ - if JPEG_MARKER_CODE.is_standalone(marker_code): - segment_length = 0 - else: - segment_length = stream.read_short(offset) - return cls(marker_code, offset, segment_length) - - @property - def marker_code(self): - """ - The single-byte code that identifies the type of this marker, e.g. - ``'\xE0'`` for start of image (SOI). - """ - return self._marker_code - - @property - def name(self): # pragma: no cover - return JPEG_MARKER_CODE.marker_names[self._marker_code] - - @property - def offset(self): # pragma: no cover - return self._offset - - @property - def segment_length(self): - """ - The length in bytes of this marker's segment - """ - return self._segment_length - - -class _App0Marker(_Marker): - """ - Represents a JFIF APP0 marker segment. - """ - def __init__( - self, marker_code, offset, length, density_units, x_density, - y_density): - super(_App0Marker, self).__init__(marker_code, offset, length) - self._density_units = density_units - self._x_density = x_density - self._y_density = y_density - - @property - def horz_dpi(self): - """ - Horizontal dots per inch specified in this marker, defaults to 72 if - not specified. - """ - return self._dpi(self._x_density) - - @property - def vert_dpi(self): - """ - Vertical dots per inch specified in this marker, defaults to 72 if - not specified. - """ - return self._dpi(self._y_density) - - def _dpi(self, density): - """ - Return dots per inch corresponding to *density* value. - """ - if self._density_units == 1: - dpi = density - elif self._density_units == 2: - dpi = int(round(density * 2.54)) - else: - dpi = 72 - return dpi - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """ - Return an |_App0Marker| instance for the APP0 marker at *offset* in - *stream*. - """ - # field off type notes - # ------------------ --- ----- ------------------- - # segment length 0 short - # JFIF identifier 2 5 chr 'JFIF\x00' - # major JPEG version 7 byte typically 1 - # minor JPEG version 8 byte typically 1 or 2 - # density units 9 byte 1=inches, 2=cm - # horz dots per unit 10 short - # vert dots per unit 12 short - # ------------------ --- ----- ------------------- - segment_length = stream.read_short(offset) - density_units = stream.read_byte(offset, 9) - x_density = stream.read_short(offset, 10) - y_density = stream.read_short(offset, 12) - return cls( - marker_code, offset, segment_length, density_units, x_density, - y_density - ) - - -class _App1Marker(_Marker): - """ - Represents a JFIF APP1 (Exif) marker segment. - """ - def __init__(self, marker_code, offset, length, horz_dpi, vert_dpi): - super(_App1Marker, self).__init__(marker_code, offset, length) - self._horz_dpi = horz_dpi - self._vert_dpi = vert_dpi - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """ - Extract the horizontal and vertical dots-per-inch value from the APP1 - header at *offset* in *stream*. - """ - # field off len type notes - # -------------------- --- --- ----- ---------------------------- - # segment length 0 2 short - # Exif identifier 2 6 6 chr 'Exif\x00\x00' - # TIFF byte order 8 2 2 chr 'II'=little 'MM'=big endian - # meaning of universe 10 2 2 chr '*\x00' or '\x00*' depending - # IFD0 off fr/II or MM 10 16 long relative to ...? - # -------------------- --- --- ----- ---------------------------- - segment_length = stream.read_short(offset) - if cls._is_non_Exif_APP1_segment(stream, offset): - return cls(marker_code, offset, segment_length, 72, 72) - tiff = cls._tiff_from_exif_segment(stream, offset, segment_length) - return cls( - marker_code, offset, segment_length, tiff.horz_dpi, tiff.vert_dpi - ) - - @property - def horz_dpi(self): - """ - Horizontal dots per inch specified in this marker, defaults to 72 if - not specified. - """ - return self._horz_dpi - - @property - def vert_dpi(self): - """ - Vertical dots per inch specified in this marker, defaults to 72 if - not specified. - """ - return self._vert_dpi - - @classmethod - def _is_non_Exif_APP1_segment(cls, stream, offset): - """ - Return True if the APP1 segment at *offset* in *stream* is NOT an - Exif segment, as determined by the ``'Exif\x00\x00'`` signature at - offset 2 in the segment. - """ - stream.seek(offset+2) - exif_signature = stream.read(6) - return exif_signature != b'Exif\x00\x00' - - @classmethod - def _tiff_from_exif_segment(cls, stream, offset, segment_length): - """ - Return a |Tiff| instance parsed from the Exif APP1 segment of - *segment_length* at *offset* in *stream*. - """ - # wrap full segment in its own stream and feed to Tiff() - stream.seek(offset+8) - segment_bytes = stream.read(segment_length-8) - substream = BytesIO(segment_bytes) - return Tiff.from_stream(substream) - - -class _SofMarker(_Marker): - """ - Represents a JFIF start of frame (SOFx) marker segment. - """ - def __init__( - self, marker_code, offset, segment_length, px_width, px_height): - super(_SofMarker, self).__init__(marker_code, offset, segment_length) - self._px_width = px_width - self._px_height = px_height - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """ - Return an |_SofMarker| instance for the SOFn marker at *offset* in - stream. - """ - # field off type notes - # ------------------ --- ----- ---------------------------- - # segment length 0 short - # Data precision 2 byte - # Vertical lines 3 short px_height - # Horizontal lines 5 short px_width - # ------------------ --- ----- ---------------------------- - segment_length = stream.read_short(offset) - px_height = stream.read_short(offset, 3) - px_width = stream.read_short(offset, 5) - return cls(marker_code, offset, segment_length, px_width, px_height) - - @property - def px_height(self): - """ - Image height in pixels - """ - return self._px_height - - @property - def px_width(self): - """ - Image width in pixels - """ - return self._px_width diff --git a/build/lib/docx/image/png.py b/build/lib/docx/image/png.py deleted file mode 100644 index c9123f76c..000000000 --- a/build/lib/docx/image/png.py +++ /dev/null @@ -1,303 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import, division, print_function - -from .constants import MIME_TYPE, PNG_CHUNK_TYPE -from .exceptions import InvalidImageStreamError -from .helpers import BIG_ENDIAN, StreamReader -from .image import BaseImageHeader - - -class Png(BaseImageHeader): - """ - Image header parser for PNG images - """ - @property - def content_type(self): - """ - MIME content type for this image, unconditionally `image/png` for - PNG images. - """ - return MIME_TYPE.PNG - - @property - def default_ext(self): - """ - Default filename extension, always 'png' for PNG images. - """ - return 'png' - - @classmethod - def from_stream(cls, stream): - """ - Return a |Png| instance having header properties parsed from image in - *stream*. - """ - parser = _PngParser.parse(stream) - - px_width = parser.px_width - px_height = parser.px_height - horz_dpi = parser.horz_dpi - vert_dpi = parser.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class _PngParser(object): - """ - Parses a PNG image stream to extract the image properties found in its - chunks. - """ - def __init__(self, chunks): - super(_PngParser, self).__init__() - self._chunks = chunks - - @classmethod - def parse(cls, stream): - """ - Return a |_PngParser| instance containing the header properties - parsed from the PNG image in *stream*. - """ - chunks = _Chunks.from_stream(stream) - return cls(chunks) - - @property - def px_width(self): - """ - The number of pixels in each row of the image. - """ - IHDR = self._chunks.IHDR - return IHDR.px_width - - @property - def px_height(self): - """ - The number of stacked rows of pixels in the image. - """ - IHDR = self._chunks.IHDR - return IHDR.px_height - - @property - def horz_dpi(self): - """ - Integer dots per inch for the width of this image. Defaults to 72 - when not present in the file, as is often the case. - """ - pHYs = self._chunks.pHYs - if pHYs is None: - return 72 - return self._dpi(pHYs.units_specifier, pHYs.horz_px_per_unit) - - @property - def vert_dpi(self): - """ - Integer dots per inch for the height of this image. Defaults to 72 - when not present in the file, as is often the case. - """ - pHYs = self._chunks.pHYs - if pHYs is None: - return 72 - return self._dpi(pHYs.units_specifier, pHYs.vert_px_per_unit) - - @staticmethod - def _dpi(units_specifier, px_per_unit): - """ - Return dots per inch value calculated from *units_specifier* and - *px_per_unit*. - """ - if units_specifier == 1 and px_per_unit: - return int(round(px_per_unit * 0.0254)) - return 72 - - -class _Chunks(object): - """ - Collection of the chunks parsed from a PNG image stream - """ - def __init__(self, chunk_iterable): - super(_Chunks, self).__init__() - self._chunks = list(chunk_iterable) - - @classmethod - def from_stream(cls, stream): - """ - Return a |_Chunks| instance containing the PNG chunks in *stream*. - """ - chunk_parser = _ChunkParser.from_stream(stream) - chunks = [chunk for chunk in chunk_parser.iter_chunks()] - return cls(chunks) - - @property - def IHDR(self): - """ - IHDR chunk in PNG image - """ - match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.IHDR - IHDR = self._find_first(match) - if IHDR is None: - raise InvalidImageStreamError('no IHDR chunk in PNG image') - return IHDR - - @property - def pHYs(self): - """ - pHYs chunk in PNG image, or |None| if not present - """ - match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.pHYs - return self._find_first(match) - - def _find_first(self, match): - """ - Return first chunk in stream order returning True for function - *match*. - """ - for chunk in self._chunks: - if match(chunk): - return chunk - return None - - -class _ChunkParser(object): - """ - Extracts chunks from a PNG image stream - """ - def __init__(self, stream_rdr): - super(_ChunkParser, self).__init__() - self._stream_rdr = stream_rdr - - @classmethod - def from_stream(cls, stream): - """ - Return a |_ChunkParser| instance that can extract the chunks from the - PNG image in *stream*. - """ - stream_rdr = StreamReader(stream, BIG_ENDIAN) - return cls(stream_rdr) - - def iter_chunks(self): - """ - Generate a |_Chunk| subclass instance for each chunk in this parser's - PNG stream, in the order encountered in the stream. - """ - for chunk_type, offset in self._iter_chunk_offsets(): - chunk = _ChunkFactory(chunk_type, self._stream_rdr, offset) - yield chunk - - def _iter_chunk_offsets(self): - """ - Generate a (chunk_type, chunk_offset) 2-tuple for each of the chunks - in the PNG image stream. Iteration stops after the IEND chunk is - returned. - """ - chunk_offset = 8 - while True: - chunk_data_len = self._stream_rdr.read_long(chunk_offset) - chunk_type = self._stream_rdr.read_str(4, chunk_offset, 4) - data_offset = chunk_offset + 8 - yield chunk_type, data_offset - if chunk_type == 'IEND': - break - # incr offset for chunk len long, chunk type, chunk data, and CRC - chunk_offset += (4 + 4 + chunk_data_len + 4) - - -def _ChunkFactory(chunk_type, stream_rdr, offset): - """ - Return a |_Chunk| subclass instance appropriate to *chunk_type* parsed - from *stream_rdr* at *offset*. - """ - chunk_cls_map = { - PNG_CHUNK_TYPE.IHDR: _IHDRChunk, - PNG_CHUNK_TYPE.pHYs: _pHYsChunk, - } - chunk_cls = chunk_cls_map.get(chunk_type, _Chunk) - return chunk_cls.from_offset(chunk_type, stream_rdr, offset) - - -class _Chunk(object): - """ - Base class for specific chunk types. Also serves as the default chunk - type. - """ - def __init__(self, chunk_type): - super(_Chunk, self).__init__() - self._chunk_type = chunk_type - - @classmethod - def from_offset(cls, chunk_type, stream_rdr, offset): - """ - Return a default _Chunk instance that only knows its chunk type. - """ - return cls(chunk_type) - - @property - def type_name(self): - """ - The chunk type name, e.g. 'IHDR', 'pHYs', etc. - """ - return self._chunk_type - - -class _IHDRChunk(_Chunk): - """ - IHDR chunk, contains the image dimensions - """ - def __init__(self, chunk_type, px_width, px_height): - super(_IHDRChunk, self).__init__(chunk_type) - self._px_width = px_width - self._px_height = px_height - - @classmethod - def from_offset(cls, chunk_type, stream_rdr, offset): - """ - Return an _IHDRChunk instance containing the image dimensions - extracted from the IHDR chunk in *stream* at *offset*. - """ - px_width = stream_rdr.read_long(offset) - px_height = stream_rdr.read_long(offset, 4) - return cls(chunk_type, px_width, px_height) - - @property - def px_width(self): - return self._px_width - - @property - def px_height(self): - return self._px_height - - -class _pHYsChunk(_Chunk): - """ - pYHs chunk, contains the image dpi information - """ - def __init__(self, chunk_type, horz_px_per_unit, vert_px_per_unit, - units_specifier): - super(_pHYsChunk, self).__init__(chunk_type) - self._horz_px_per_unit = horz_px_per_unit - self._vert_px_per_unit = vert_px_per_unit - self._units_specifier = units_specifier - - @classmethod - def from_offset(cls, chunk_type, stream_rdr, offset): - """ - Return a _pHYsChunk instance containing the image resolution - extracted from the pHYs chunk in *stream* at *offset*. - """ - horz_px_per_unit = stream_rdr.read_long(offset) - vert_px_per_unit = stream_rdr.read_long(offset, 4) - units_specifier = stream_rdr.read_byte(offset, 8) - return cls( - chunk_type, horz_px_per_unit, vert_px_per_unit, units_specifier - ) - - @property - def horz_px_per_unit(self): - return self._horz_px_per_unit - - @property - def vert_px_per_unit(self): - return self._vert_px_per_unit - - @property - def units_specifier(self): - return self._units_specifier diff --git a/build/lib/docx/image/tiff.py b/build/lib/docx/image/tiff.py deleted file mode 100644 index c38242360..000000000 --- a/build/lib/docx/image/tiff.py +++ /dev/null @@ -1,345 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import, division, print_function - -from .constants import MIME_TYPE, TIFF_FLD, TIFF_TAG -from .helpers import BIG_ENDIAN, LITTLE_ENDIAN, StreamReader -from .image import BaseImageHeader - - -class Tiff(BaseImageHeader): - """ - Image header parser for TIFF images. Handles both big and little endian - byte ordering. - """ - @property - def content_type(self): - """ - Return the MIME type of this TIFF image, unconditionally the string - ``image/tiff``. - """ - return MIME_TYPE.TIFF - - @property - def default_ext(self): - """ - Default filename extension, always 'tiff' for TIFF images. - """ - return 'tiff' - - @classmethod - def from_stream(cls, stream): - """ - Return a |Tiff| instance containing the properties of the TIFF image - in *stream*. - """ - parser = _TiffParser.parse(stream) - - px_width = parser.px_width - px_height = parser.px_height - horz_dpi = parser.horz_dpi - vert_dpi = parser.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class _TiffParser(object): - """ - Parses a TIFF image stream to extract the image properties found in its - main image file directory (IFD) - """ - def __init__(self, ifd_entries): - super(_TiffParser, self).__init__() - self._ifd_entries = ifd_entries - - @classmethod - def parse(cls, stream): - """ - Return an instance of |_TiffParser| containing the properties parsed - from the TIFF image in *stream*. - """ - stream_rdr = cls._make_stream_reader(stream) - ifd0_offset = stream_rdr.read_long(4) - ifd_entries = _IfdEntries.from_stream(stream_rdr, ifd0_offset) - return cls(ifd_entries) - - @property - def horz_dpi(self): - """ - The horizontal dots per inch value calculated from the XResolution - and ResolutionUnit tags of the IFD; defaults to 72 if those tags are - not present. - """ - return self._dpi(TIFF_TAG.X_RESOLUTION) - - @property - def vert_dpi(self): - """ - The vertical dots per inch value calculated from the XResolution and - ResolutionUnit tags of the IFD; defaults to 72 if those tags are not - present. - """ - return self._dpi(TIFF_TAG.Y_RESOLUTION) - - @property - def px_height(self): - """ - The number of stacked rows of pixels in the image, |None| if the IFD - contains no ``ImageLength`` tag, the expected case when the TIFF is - embeded in an Exif image. - """ - return self._ifd_entries.get(TIFF_TAG.IMAGE_LENGTH) - - @property - def px_width(self): - """ - The number of pixels in each row in the image, |None| if the IFD - contains no ``ImageWidth`` tag, the expected case when the TIFF is - embeded in an Exif image. - """ - return self._ifd_entries.get(TIFF_TAG.IMAGE_WIDTH) - - @classmethod - def _detect_endian(cls, stream): - """ - Return either BIG_ENDIAN or LITTLE_ENDIAN depending on the endian - indicator found in the TIFF *stream* header, either 'MM' or 'II'. - """ - stream.seek(0) - endian_str = stream.read(2) - return BIG_ENDIAN if endian_str == b'MM' else LITTLE_ENDIAN - - def _dpi(self, resolution_tag): - """ - Return the dpi value calculated for *resolution_tag*, which can be - either TIFF_TAG.X_RESOLUTION or TIFF_TAG.Y_RESOLUTION. The - calculation is based on the values of both that tag and the - TIFF_TAG.RESOLUTION_UNIT tag in this parser's |_IfdEntries| instance. - """ - ifd_entries = self._ifd_entries - - if resolution_tag not in ifd_entries: - return 72 - - # resolution unit defaults to inches (2) - resolution_unit = ( - ifd_entries[TIFF_TAG.RESOLUTION_UNIT] - if TIFF_TAG.RESOLUTION_UNIT in ifd_entries else 2 - ) - - if resolution_unit == 1: # aspect ratio only - return 72 - # resolution_unit == 2 for inches, 3 for centimeters - units_per_inch = 1 if resolution_unit == 2 else 2.54 - dots_per_unit = ifd_entries[resolution_tag] - return int(round(dots_per_unit * units_per_inch)) - - @classmethod - def _make_stream_reader(cls, stream): - """ - Return a |StreamReader| instance with wrapping *stream* and having - "endian-ness" determined by the 'MM' or 'II' indicator in the TIFF - stream header. - """ - endian = cls._detect_endian(stream) - return StreamReader(stream, endian) - - -class _IfdEntries(object): - """ - Image File Directory for a TIFF image, having mapping (dict) semantics - allowing "tag" values to be retrieved by tag code. - """ - def __init__(self, entries): - super(_IfdEntries, self).__init__() - self._entries = entries - - def __contains__(self, key): - """ - Provides ``in`` operator, e.g. ``tag in ifd_entries`` - """ - return self._entries.__contains__(key) - - def __getitem__(self, key): - """ - Provides indexed access, e.g. ``tag_value = ifd_entries[tag_code]`` - """ - return self._entries.__getitem__(key) - - @classmethod - def from_stream(cls, stream, offset): - """ - Return a new |_IfdEntries| instance parsed from *stream* starting at - *offset*. - """ - ifd_parser = _IfdParser(stream, offset) - entries = dict((e.tag, e.value) for e in ifd_parser.iter_entries()) - return cls(entries) - - def get(self, tag_code, default=None): - """ - Return value of IFD entry having tag matching *tag_code*, or - *default* if no matching tag found. - """ - return self._entries.get(tag_code, default) - - -class _IfdParser(object): - """ - Service object that knows how to extract directory entries from an Image - File Directory (IFD) - """ - def __init__(self, stream_rdr, offset): - super(_IfdParser, self).__init__() - self._stream_rdr = stream_rdr - self._offset = offset - - def iter_entries(self): - """ - Generate an |_IfdEntry| instance corresponding to each entry in the - directory. - """ - for idx in range(self._entry_count): - dir_entry_offset = self._offset + 2 + (idx*12) - ifd_entry = _IfdEntryFactory(self._stream_rdr, dir_entry_offset) - yield ifd_entry - - @property - def _entry_count(self): - """ - The count of directory entries, read from the top of the IFD header - """ - return self._stream_rdr.read_short(self._offset) - - -def _IfdEntryFactory(stream_rdr, offset): - """ - Return an |_IfdEntry| subclass instance containing the value of the - directory entry at *offset* in *stream_rdr*. - """ - ifd_entry_classes = { - TIFF_FLD.ASCII: _AsciiIfdEntry, - TIFF_FLD.SHORT: _ShortIfdEntry, - TIFF_FLD.LONG: _LongIfdEntry, - TIFF_FLD.RATIONAL: _RationalIfdEntry, - } - field_type = stream_rdr.read_short(offset, 2) - if field_type in ifd_entry_classes: - entry_cls = ifd_entry_classes[field_type] - else: - entry_cls = _IfdEntry - return entry_cls.from_stream(stream_rdr, offset) - - -class _IfdEntry(object): - """ - Base class for IFD entry classes. Subclasses are differentiated by value - type, e.g. ASCII, long int, etc. - """ - def __init__(self, tag_code, value): - super(_IfdEntry, self).__init__() - self._tag_code = tag_code - self._value = value - - @classmethod - def from_stream(cls, stream_rdr, offset): - """ - Return an |_IfdEntry| subclass instance containing the tag and value - of the tag parsed from *stream_rdr* at *offset*. Note this method is - common to all subclasses. Override the ``_parse_value()`` method to - provide distinctive behavior based on field type. - """ - tag_code = stream_rdr.read_short(offset, 0) - value_count = stream_rdr.read_long(offset, 4) - value_offset = stream_rdr.read_long(offset, 8) - value = cls._parse_value( - stream_rdr, offset, value_count, value_offset - ) - return cls(tag_code, value) - - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """ - Return the value of this field parsed from *stream_rdr* at *offset*. - Intended to be overridden by subclasses. - """ - return 'UNIMPLEMENTED FIELD TYPE' # pragma: no cover - - @property - def tag(self): - """ - Short int code that identifies this IFD entry - """ - return self._tag_code - - @property - def value(self): - """ - Value of this tag, its type being dependent on the tag. - """ - return self._value - - -class _AsciiIfdEntry(_IfdEntry): - """ - IFD entry having the form of a NULL-terminated ASCII string - """ - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """ - Return the ASCII string parsed from *stream_rdr* at *value_offset*. - The length of the string, including a terminating '\x00' (NUL) - character, is in *value_count*. - """ - return stream_rdr.read_str(value_count-1, value_offset) - - -class _ShortIfdEntry(_IfdEntry): - """ - IFD entry expressed as a short (2-byte) integer - """ - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """ - Return the short int value contained in the *value_offset* field of - this entry. Only supports single values at present. - """ - if value_count == 1: - return stream_rdr.read_short(offset, 8) - else: # pragma: no cover - return 'Multi-value short integer NOT IMPLEMENTED' - - -class _LongIfdEntry(_IfdEntry): - """ - IFD entry expressed as a long (4-byte) integer - """ - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """ - Return the long int value contained in the *value_offset* field of - this entry. Only supports single values at present. - """ - if value_count == 1: - return stream_rdr.read_long(offset, 8) - else: # pragma: no cover - return 'Multi-value long integer NOT IMPLEMENTED' - - -class _RationalIfdEntry(_IfdEntry): - """ - IFD entry expressed as a numerator, denominator pair - """ - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """ - Return the rational (numerator / denominator) value at *value_offset* - in *stream_rdr* as a floating-point number. Only supports single - values at present. - """ - if value_count == 1: - numerator = stream_rdr.read_long(value_offset) - denominator = stream_rdr.read_long(value_offset, 4) - return numerator / denominator - else: # pragma: no cover - return 'Multi-value Rational NOT IMPLEMENTED' diff --git a/build/lib/docx/opc/__init__.py b/build/lib/docx/opc/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/lib/docx/opc/compat.py b/build/lib/docx/opc/compat.py deleted file mode 100644 index d944fe43b..000000000 --- a/build/lib/docx/opc/compat.py +++ /dev/null @@ -1,50 +0,0 @@ -# encoding: utf-8 - -""" -Provides Python 2/3 compatibility objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -import sys - -# =========================================================================== -# Python 3 versions -# =========================================================================== - -if sys.version_info >= (3, 0): - - def cls_method_fn(cls, method_name): - """ - Return the function object associated with the method of *cls* having - *method_name*. - """ - return getattr(cls, method_name) - - def is_string(obj): - """ - Return True if *obj* is a string, False otherwise. - """ - return isinstance(obj, str) - -# =========================================================================== -# Python 2 versions -# =========================================================================== - -else: - - def cls_method_fn(cls, method_name): - """ - Return the function object associated with the method of *cls* having - *method_name*. - """ - unbound_method = getattr(cls, method_name) - return unbound_method.__func__ - - def is_string(obj): - """ - Return True if *obj* is a string, False otherwise. - """ - return isinstance(obj, basestring) diff --git a/build/lib/docx/opc/constants.py b/build/lib/docx/opc/constants.py deleted file mode 100644 index b90aa394a..000000000 --- a/build/lib/docx/opc/constants.py +++ /dev/null @@ -1,658 +0,0 @@ -# encoding: utf-8 - -""" -Constant values related to the Open Packaging Convention, in particular, -content types and relationship types. -""" - - -class CONTENT_TYPE(object): - """ - Content type URIs (like MIME-types) that specify a part's format - """ - BMP = ( - 'image/bmp' - ) - DML_CHART = ( - 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml' - ) - DML_CHARTSHAPES = ( - 'application/vnd.openxmlformats-officedocument.drawingml.chartshapes' - '+xml' - ) - DML_DIAGRAM_COLORS = ( - 'application/vnd.openxmlformats-officedocument.drawingml.diagramColo' - 'rs+xml' - ) - DML_DIAGRAM_DATA = ( - 'application/vnd.openxmlformats-officedocument.drawingml.diagramData' - '+xml' - ) - DML_DIAGRAM_LAYOUT = ( - 'application/vnd.openxmlformats-officedocument.drawingml.diagramLayo' - 'ut+xml' - ) - DML_DIAGRAM_STYLE = ( - 'application/vnd.openxmlformats-officedocument.drawingml.diagramStyl' - 'e+xml' - ) - GIF = ( - 'image/gif' - ) - JPEG = ( - 'image/jpeg' - ) - MS_PHOTO = ( - 'image/vnd.ms-photo' - ) - OFC_CUSTOM_PROPERTIES = ( - 'application/vnd.openxmlformats-officedocument.custom-properties+xml' - ) - OFC_CUSTOM_XML_PROPERTIES = ( - 'application/vnd.openxmlformats-officedocument.customXmlProperties+x' - 'ml' - ) - OFC_DRAWING = ( - 'application/vnd.openxmlformats-officedocument.drawing+xml' - ) - OFC_EXTENDED_PROPERTIES = ( - 'application/vnd.openxmlformats-officedocument.extended-properties+x' - 'ml' - ) - OFC_OLE_OBJECT = ( - 'application/vnd.openxmlformats-officedocument.oleObject' - ) - OFC_PACKAGE = ( - 'application/vnd.openxmlformats-officedocument.package' - ) - OFC_THEME = ( - 'application/vnd.openxmlformats-officedocument.theme+xml' - ) - OFC_THEME_OVERRIDE = ( - 'application/vnd.openxmlformats-officedocument.themeOverride+xml' - ) - OFC_VML_DRAWING = ( - 'application/vnd.openxmlformats-officedocument.vmlDrawing' - ) - OPC_CORE_PROPERTIES = ( - 'application/vnd.openxmlformats-package.core-properties+xml' - ) - OPC_DIGITAL_SIGNATURE_CERTIFICATE = ( - 'application/vnd.openxmlformats-package.digital-signature-certificat' - 'e' - ) - OPC_DIGITAL_SIGNATURE_ORIGIN = ( - 'application/vnd.openxmlformats-package.digital-signature-origin' - ) - OPC_DIGITAL_SIGNATURE_XMLSIGNATURE = ( - 'application/vnd.openxmlformats-package.digital-signature-xmlsignatu' - 're+xml' - ) - OPC_RELATIONSHIPS = ( - 'application/vnd.openxmlformats-package.relationships+xml' - ) - PML_COMMENTS = ( - 'application/vnd.openxmlformats-officedocument.presentationml.commen' - 'ts+xml' - ) - PML_COMMENT_AUTHORS = ( - 'application/vnd.openxmlformats-officedocument.presentationml.commen' - 'tAuthors+xml' - ) - PML_HANDOUT_MASTER = ( - 'application/vnd.openxmlformats-officedocument.presentationml.handou' - 'tMaster+xml' - ) - PML_NOTES_MASTER = ( - 'application/vnd.openxmlformats-officedocument.presentationml.notesM' - 'aster+xml' - ) - PML_NOTES_SLIDE = ( - 'application/vnd.openxmlformats-officedocument.presentationml.notesS' - 'lide+xml' - ) - PML_PRESENTATION_MAIN = ( - 'application/vnd.openxmlformats-officedocument.presentationml.presen' - 'tation.main+xml' - ) - PML_PRES_PROPS = ( - 'application/vnd.openxmlformats-officedocument.presentationml.presPr' - 'ops+xml' - ) - PML_PRINTER_SETTINGS = ( - 'application/vnd.openxmlformats-officedocument.presentationml.printe' - 'rSettings' - ) - PML_SLIDE = ( - 'application/vnd.openxmlformats-officedocument.presentationml.slide+' - 'xml' - ) - PML_SLIDESHOW_MAIN = ( - 'application/vnd.openxmlformats-officedocument.presentationml.slides' - 'how.main+xml' - ) - PML_SLIDE_LAYOUT = ( - 'application/vnd.openxmlformats-officedocument.presentationml.slideL' - 'ayout+xml' - ) - PML_SLIDE_MASTER = ( - 'application/vnd.openxmlformats-officedocument.presentationml.slideM' - 'aster+xml' - ) - PML_SLIDE_UPDATE_INFO = ( - 'application/vnd.openxmlformats-officedocument.presentationml.slideU' - 'pdateInfo+xml' - ) - PML_TABLE_STYLES = ( - 'application/vnd.openxmlformats-officedocument.presentationml.tableS' - 'tyles+xml' - ) - PML_TAGS = ( - 'application/vnd.openxmlformats-officedocument.presentationml.tags+x' - 'ml' - ) - PML_TEMPLATE_MAIN = ( - 'application/vnd.openxmlformats-officedocument.presentationml.templa' - 'te.main+xml' - ) - PML_VIEW_PROPS = ( - 'application/vnd.openxmlformats-officedocument.presentationml.viewPr' - 'ops+xml' - ) - PNG = ( - 'image/png' - ) - SML_CALC_CHAIN = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.calcCha' - 'in+xml' - ) - SML_CHARTSHEET = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsh' - 'eet+xml' - ) - SML_COMMENTS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.comment' - 's+xml' - ) - SML_CONNECTIONS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.connect' - 'ions+xml' - ) - SML_CUSTOM_PROPERTY = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.customP' - 'roperty' - ) - SML_DIALOGSHEET = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.dialogs' - 'heet+xml' - ) - SML_EXTERNAL_LINK = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.externa' - 'lLink+xml' - ) - SML_PIVOT_CACHE_DEFINITION = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCa' - 'cheDefinition+xml' - ) - SML_PIVOT_CACHE_RECORDS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCa' - 'cheRecords+xml' - ) - SML_PIVOT_TABLE = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTa' - 'ble+xml' - ) - SML_PRINTER_SETTINGS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.printer' - 'Settings' - ) - SML_QUERY_TABLE = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.queryTa' - 'ble+xml' - ) - SML_REVISION_HEADERS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisio' - 'nHeaders+xml' - ) - SML_REVISION_LOG = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisio' - 'nLog+xml' - ) - SML_SHARED_STRINGS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedS' - 'trings+xml' - ) - SML_SHEET = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - ) - SML_SHEET_MAIN = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.m' - 'ain+xml' - ) - SML_SHEET_METADATA = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMe' - 'tadata+xml' - ) - SML_STYLES = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+' - 'xml' - ) - SML_TABLE = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+x' - 'ml' - ) - SML_TABLE_SINGLE_CELLS = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.tableSi' - 'ngleCells+xml' - ) - SML_TEMPLATE_MAIN = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.templat' - 'e.main+xml' - ) - SML_USER_NAMES = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.userNam' - 'es+xml' - ) - SML_VOLATILE_DEPENDENCIES = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.volatil' - 'eDependencies+xml' - ) - SML_WORKSHEET = ( - 'application/vnd.openxmlformats-officedocument.spreadsheetml.workshe' - 'et+xml' - ) - TIFF = ( - 'image/tiff' - ) - WML_COMMENTS = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.comm' - 'ents+xml' - ) - WML_DOCUMENT = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.docu' - 'ment' - ) - WML_DOCUMENT_GLOSSARY = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.docu' - 'ment.glossary+xml' - ) - WML_DOCUMENT_MAIN = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.docu' - 'ment.main+xml' - ) - WML_ENDNOTES = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.endn' - 'otes+xml' - ) - WML_FONT_TABLE = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.font' - 'Table+xml' - ) - WML_FOOTER = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.foot' - 'er+xml' - ) - WML_FOOTNOTES = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.foot' - 'notes+xml' - ) - WML_HEADER = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.head' - 'er+xml' - ) - WML_NUMBERING = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.numb' - 'ering+xml' - ) - WML_PRINTER_SETTINGS = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.prin' - 'terSettings' - ) - WML_SETTINGS = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.sett' - 'ings+xml' - ) - WML_STYLES = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.styl' - 'es+xml' - ) - WML_WEB_SETTINGS = ( - 'application/vnd.openxmlformats-officedocument.wordprocessingml.webS' - 'ettings+xml' - ) - XML = ( - 'application/xml' - ) - X_EMF = ( - 'image/x-emf' - ) - X_FONTDATA = ( - 'application/x-fontdata' - ) - X_FONT_TTF = ( - 'application/x-font-ttf' - ) - X_WMF = ( - 'image/x-wmf' - ) - - -class NAMESPACE(object): - """Constant values for OPC XML namespaces""" - DML_WORDPROCESSING_DRAWING = ( - 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDraw' - 'ing' - ) - OFC_RELATIONSHIPS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - ) - OPC_RELATIONSHIPS = ( - 'http://schemas.openxmlformats.org/package/2006/relationships' - ) - OPC_CONTENT_TYPES = ( - 'http://schemas.openxmlformats.org/package/2006/content-types' - ) - WML_MAIN = ( - 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' - ) - - -class RELATIONSHIP_TARGET_MODE(object): - """Open XML relationship target modes""" - EXTERNAL = 'External' - INTERNAL = 'Internal' - - -class RELATIONSHIP_TYPE(object): - AUDIO = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/audio' - ) - A_F_CHUNK = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/aFChunk' - ) - CALC_CHAIN = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/calcChain' - ) - CERTIFICATE = ( - 'http://schemas.openxmlformats.org/package/2006/relationships/digita' - 'l-signature/certificate' - ) - CHART = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/chart' - ) - CHARTSHEET = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/chartsheet' - ) - CHART_USER_SHAPES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/chartUserShapes' - ) - COMMENTS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/comments' - ) - COMMENT_AUTHORS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/commentAuthors' - ) - CONNECTIONS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/connections' - ) - CONTROL = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/control' - ) - CORE_PROPERTIES = ( - 'http://schemas.openxmlformats.org/package/2006/relationships/metada' - 'ta/core-properties' - ) - CUSTOM_PROPERTIES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/custom-properties' - ) - CUSTOM_PROPERTY = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/customProperty' - ) - CUSTOM_XML = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/customXml' - ) - CUSTOM_XML_PROPS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/customXmlProps' - ) - DIAGRAM_COLORS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/diagramColors' - ) - DIAGRAM_DATA = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/diagramData' - ) - DIAGRAM_LAYOUT = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/diagramLayout' - ) - DIAGRAM_QUICK_STYLE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/diagramQuickStyle' - ) - DIALOGSHEET = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/dialogsheet' - ) - DRAWING = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/drawing' - ) - ENDNOTES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/endnotes' - ) - EXTENDED_PROPERTIES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/extended-properties' - ) - EXTERNAL_LINK = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/externalLink' - ) - FONT = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/font' - ) - FONT_TABLE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/fontTable' - ) - FOOTER = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/footer' - ) - FOOTNOTES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/footnotes' - ) - GLOSSARY_DOCUMENT = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/glossaryDocument' - ) - HANDOUT_MASTER = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/handoutMaster' - ) - HEADER = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/header' - ) - HYPERLINK = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/hyperlink' - ) - IMAGE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/image' - ) - NOTES_MASTER = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/notesMaster' - ) - NOTES_SLIDE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/notesSlide' - ) - NUMBERING = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/numbering' - ) - OFFICE_DOCUMENT = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/officeDocument' - ) - OLE_OBJECT = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/oleObject' - ) - ORIGIN = ( - 'http://schemas.openxmlformats.org/package/2006/relationships/digita' - 'l-signature/origin' - ) - PACKAGE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/package' - ) - PIVOT_CACHE_DEFINITION = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/pivotCacheDefinition' - ) - PIVOT_CACHE_RECORDS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/spreadsheetml/pivotCacheRecords' - ) - PIVOT_TABLE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/pivotTable' - ) - PRES_PROPS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/presProps' - ) - PRINTER_SETTINGS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/printerSettings' - ) - QUERY_TABLE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/queryTable' - ) - REVISION_HEADERS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/revisionHeaders' - ) - REVISION_LOG = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/revisionLog' - ) - SETTINGS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/settings' - ) - SHARED_STRINGS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/sharedStrings' - ) - SHEET_METADATA = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/sheetMetadata' - ) - SIGNATURE = ( - 'http://schemas.openxmlformats.org/package/2006/relationships/digita' - 'l-signature/signature' - ) - SLIDE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/slide' - ) - SLIDE_LAYOUT = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/slideLayout' - ) - SLIDE_MASTER = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/slideMaster' - ) - SLIDE_UPDATE_INFO = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/slideUpdateInfo' - ) - STYLES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/styles' - ) - TABLE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/table' - ) - TABLE_SINGLE_CELLS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/tableSingleCells' - ) - TABLE_STYLES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/tableStyles' - ) - TAGS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/tags' - ) - THEME = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/theme' - ) - THEME_OVERRIDE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/themeOverride' - ) - THUMBNAIL = ( - 'http://schemas.openxmlformats.org/package/2006/relationships/metada' - 'ta/thumbnail' - ) - USERNAMES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/usernames' - ) - VIDEO = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/video' - ) - VIEW_PROPS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/viewProps' - ) - VML_DRAWING = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/vmlDrawing' - ) - VOLATILE_DEPENDENCIES = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/volatileDependencies' - ) - WEB_SETTINGS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/webSettings' - ) - WORKSHEET_SOURCE = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/worksheetSource' - ) - XML_MAPS = ( - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - '/xmlMaps' - ) diff --git a/build/lib/docx/opc/coreprops.py b/build/lib/docx/opc/coreprops.py deleted file mode 100644 index 2d38dabd3..000000000 --- a/build/lib/docx/opc/coreprops.py +++ /dev/null @@ -1,139 +0,0 @@ -# encoding: utf-8 - -""" -The :mod:`pptx.packaging` module coheres around the concerns of reading and -writing presentations to and from a .pptx file. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - - -class CoreProperties(object): - """ - Corresponds to part named ``/docProps/core.xml``, containing the core - document properties for this document package. - """ - def __init__(self, element): - self._element = element - - @property - def author(self): - return self._element.author_text - - @author.setter - def author(self, value): - self._element.author_text = value - - @property - def category(self): - return self._element.category_text - - @category.setter - def category(self, value): - self._element.category_text = value - - @property - def comments(self): - return self._element.comments_text - - @comments.setter - def comments(self, value): - self._element.comments_text = value - - @property - def content_status(self): - return self._element.contentStatus_text - - @content_status.setter - def content_status(self, value): - self._element.contentStatus_text = value - - @property - def created(self): - return self._element.created_datetime - - @created.setter - def created(self, value): - self._element.created_datetime = value - - @property - def identifier(self): - return self._element.identifier_text - - @identifier.setter - def identifier(self, value): - self._element.identifier_text = value - - @property - def keywords(self): - return self._element.keywords_text - - @keywords.setter - def keywords(self, value): - self._element.keywords_text = value - - @property - def language(self): - return self._element.language_text - - @language.setter - def language(self, value): - self._element.language_text = value - - @property - def last_modified_by(self): - return self._element.lastModifiedBy_text - - @last_modified_by.setter - def last_modified_by(self, value): - self._element.lastModifiedBy_text = value - - @property - def last_printed(self): - return self._element.lastPrinted_datetime - - @last_printed.setter - def last_printed(self, value): - self._element.lastPrinted_datetime = value - - @property - def modified(self): - return self._element.modified_datetime - - @modified.setter - def modified(self, value): - self._element.modified_datetime = value - - @property - def revision(self): - return self._element.revision_number - - @revision.setter - def revision(self, value): - self._element.revision_number = value - - @property - def subject(self): - return self._element.subject_text - - @subject.setter - def subject(self, value): - self._element.subject_text = value - - @property - def title(self): - return self._element.title_text - - @title.setter - def title(self, value): - self._element.title_text = value - - @property - def version(self): - return self._element.version_text - - @version.setter - def version(self, value): - self._element.version_text = value diff --git a/build/lib/docx/opc/exceptions.py b/build/lib/docx/opc/exceptions.py deleted file mode 100644 index b8e6de43f..000000000 --- a/build/lib/docx/opc/exceptions.py +++ /dev/null @@ -1,19 +0,0 @@ -# encoding: utf-8 - -""" -Exceptions specific to python-opc - -The base exception class is OpcError. -""" - - -class OpcError(Exception): - """ - Base error class for python-opc - """ - - -class PackageNotFoundError(OpcError): - """ - Raised when a package cannot be found at the specified path. - """ diff --git a/build/lib/docx/opc/oxml.py b/build/lib/docx/opc/oxml.py deleted file mode 100644 index 494b31dca..000000000 --- a/build/lib/docx/opc/oxml.py +++ /dev/null @@ -1,292 +0,0 @@ -# encoding: utf-8 - -""" -Temporary stand-in for main oxml module that came across with the -PackageReader transplant. Probably much will get replaced with objects from -the pptx.oxml.core and then this module will either get deleted or only hold -the package related custom element classes. -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from lxml import etree - -from .constants import NAMESPACE as NS, RELATIONSHIP_TARGET_MODE as RTM - - -# configure XML parser -element_class_lookup = etree.ElementNamespaceClassLookup() -oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) -oxml_parser.set_element_class_lookup(element_class_lookup) - -nsmap = { - 'ct': NS.OPC_CONTENT_TYPES, - 'pr': NS.OPC_RELATIONSHIPS, - 'r': NS.OFC_RELATIONSHIPS, -} - - -# =========================================================================== -# functions -# =========================================================================== - -def parse_xml(text): - """ - ``etree.fromstring()`` replacement that uses oxml parser - """ - return etree.fromstring(text, oxml_parser) - - -def qn(tag): - """ - Stands for "qualified name", a utility function to turn a namespace - prefixed tag name into a Clark-notation qualified tag name for lxml. For - example, ``qn('p:cSld')`` returns ``'{http://schemas.../main}cSld'``. - """ - prefix, tagroot = tag.split(':') - uri = nsmap[prefix] - return '{%s}%s' % (uri, tagroot) - - -def serialize_part_xml(part_elm): - """ - Serialize *part_elm* etree element to XML suitable for storage as an XML - part. That is to say, no insignificant whitespace added for readability, - and an appropriate XML declaration added with UTF-8 encoding specified. - """ - return etree.tostring(part_elm, encoding='UTF-8', standalone=True) - - -def serialize_for_reading(element): - """ - Serialize *element* to human-readable XML suitable for tests. No XML - declaration. - """ - return etree.tostring(element, encoding='unicode', pretty_print=True) - - -# =========================================================================== -# Custom element classes -# =========================================================================== - -class BaseOxmlElement(etree.ElementBase): - """ - Base class for all custom element classes, to add standardized behavior - to all classes in one place. - """ - @property - def xml(self): - """ - Return XML string for this element, suitable for testing purposes. - Pretty printed for readability and without an XML declaration at the - top. - """ - return serialize_for_reading(self) - - -class CT_Default(BaseOxmlElement): - """ - ```` element, specifying the default content type to be applied - to a part with the specified extension. - """ - @property - def content_type(self): - """ - String held in the ``ContentType`` attribute of this ```` - element. - """ - return self.get('ContentType') - - @property - def extension(self): - """ - String held in the ``Extension`` attribute of this ```` - element. - """ - return self.get('Extension') - - @staticmethod - def new(ext, content_type): - """ - Return a new ```` element with attributes set to parameter - values. - """ - xml = '' % nsmap['ct'] - default = parse_xml(xml) - default.set('Extension', ext) - default.set('ContentType', content_type) - return default - - -class CT_Override(BaseOxmlElement): - """ - ```` element, specifying the content type to be applied for a - part with the specified partname. - """ - @property - def content_type(self): - """ - String held in the ``ContentType`` attribute of this ```` - element. - """ - return self.get('ContentType') - - @staticmethod - def new(partname, content_type): - """ - Return a new ```` element with attributes set to parameter - values. - """ - xml = '' % nsmap['ct'] - override = parse_xml(xml) - override.set('PartName', partname) - override.set('ContentType', content_type) - return override - - @property - def partname(self): - """ - String held in the ``PartName`` attribute of this ```` - element. - """ - return self.get('PartName') - - -class CT_Relationship(BaseOxmlElement): - """ - ```` element, representing a single relationship from a - source to a target part. - """ - @staticmethod - def new(rId, reltype, target, target_mode=RTM.INTERNAL): - """ - Return a new ```` element. - """ - xml = '' % nsmap['pr'] - relationship = parse_xml(xml) - relationship.set('Id', rId) - relationship.set('Type', reltype) - relationship.set('Target', target) - if target_mode == RTM.EXTERNAL: - relationship.set('TargetMode', RTM.EXTERNAL) - return relationship - - @property - def rId(self): - """ - String held in the ``Id`` attribute of this ```` - element. - """ - return self.get('Id') - - @property - def reltype(self): - """ - String held in the ``Type`` attribute of this ```` - element. - """ - return self.get('Type') - - @property - def target_ref(self): - """ - String held in the ``Target`` attribute of this ```` - element. - """ - return self.get('Target') - - @property - def target_mode(self): - """ - String held in the ``TargetMode`` attribute of this - ```` element, either ``Internal`` or ``External``. - Defaults to ``Internal``. - """ - return self.get('TargetMode', RTM.INTERNAL) - - -class CT_Relationships(BaseOxmlElement): - """ - ```` element, the root element in a .rels file. - """ - def add_rel(self, rId, reltype, target, is_external=False): - """ - Add a child ```` element with attributes set according - to parameter values. - """ - target_mode = RTM.EXTERNAL if is_external else RTM.INTERNAL - relationship = CT_Relationship.new(rId, reltype, target, target_mode) - self.append(relationship) - - @staticmethod - def new(): - """ - Return a new ```` element. - """ - xml = '' % nsmap['pr'] - relationships = parse_xml(xml) - return relationships - - @property - def Relationship_lst(self): - """ - Return a list containing all the ```` child elements. - """ - return self.findall(qn('pr:Relationship')) - - @property - def xml(self): - """ - Return XML string for this element, suitable for saving in a .rels - stream, not pretty printed and with an XML declaration at the top. - """ - return serialize_part_xml(self) - - -class CT_Types(BaseOxmlElement): - """ - ```` element, the container element for Default and Override - elements in [Content_Types].xml. - """ - def add_default(self, ext, content_type): - """ - Add a child ```` element with attributes set to parameter - values. - """ - default = CT_Default.new(ext, content_type) - self.append(default) - - def add_override(self, partname, content_type): - """ - Add a child ```` element with attributes set to parameter - values. - """ - override = CT_Override.new(partname, content_type) - self.append(override) - - @property - def defaults(self): - return self.findall(qn('ct:Default')) - - @staticmethod - def new(): - """ - Return a new ```` element. - """ - xml = '' % nsmap['ct'] - types = parse_xml(xml) - return types - - @property - def overrides(self): - return self.findall(qn('ct:Override')) - - -ct_namespace = element_class_lookup.get_namespace(nsmap['ct']) -ct_namespace['Default'] = CT_Default -ct_namespace['Override'] = CT_Override -ct_namespace['Types'] = CT_Types - -pr_namespace = element_class_lookup.get_namespace(nsmap['pr']) -pr_namespace['Relationship'] = CT_Relationship -pr_namespace['Relationships'] = CT_Relationships diff --git a/build/lib/docx/opc/package.py b/build/lib/docx/opc/package.py deleted file mode 100644 index 4951c2319..000000000 --- a/build/lib/docx/opc/package.py +++ /dev/null @@ -1,249 +0,0 @@ -# encoding: utf-8 - -""" -The :mod:`pptx.packaging` module coheres around the concerns of reading and -writing presentations to and from a .pptx file. -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from .constants import RELATIONSHIP_TYPE as RT -from .packuri import PACKAGE_URI -from .part import PartFactory -from .parts.coreprops import CorePropertiesPart -from docx.parts.comments import CommentsPart -from ..parts.footnotes import FootnotesPart -from .pkgreader import PackageReader -from .pkgwriter import PackageWriter -from .rel import Relationships -from .shared import lazyproperty - - -class OpcPackage(object): - """ - Main API class for |python-opc|. A new instance is constructed by calling - the :meth:`open` class method with a path to a package file or file-like - object containing one. - """ - def __init__(self): - super(OpcPackage, self).__init__() - - def after_unmarshal(self): - """ - Entry point for any post-unmarshaling processing. May be overridden - by subclasses without forwarding call to super. - """ - # don't place any code here, just catch call if not overridden by - # subclass - pass - - @property - def core_properties(self): - """ - |CoreProperties| object providing read/write access to the Dublin - Core properties for this document. - """ - return self._core_properties_part.core_properties - - def iter_rels(self): - """ - Generate exactly one reference to each relationship in the package by - performing a depth-first traversal of the rels graph. - """ - def walk_rels(source, visited=None): - visited = [] if visited is None else visited - for rel in source.rels.values(): - yield rel - if rel.is_external: - continue - part = rel.target_part - if part in visited: - continue - visited.append(part) - new_source = part - for rel in walk_rels(new_source, visited): - yield rel - - for rel in walk_rels(self): - yield rel - - def iter_parts(self): - """ - Generate exactly one reference to each of the parts in the package by - performing a depth-first traversal of the rels graph. - """ - def walk_parts(source, visited=list()): - for rel in source.rels.values(): - if rel.is_external: - continue - part = rel.target_part - if part in visited: - continue - visited.append(part) - yield part - new_source = part - for part in walk_parts(new_source, visited): - yield part - - for part in walk_parts(self): - yield part - - def load_rel(self, reltype, target, rId, is_external=False): - """ - Return newly added |_Relationship| instance of *reltype* between this - part and *target* with key *rId*. Target mode is set to - ``RTM.EXTERNAL`` if *is_external* is |True|. Intended for use during - load from a serialized package, where the rId is well known. Other - methods exist for adding a new relationship to the package during - processing. - """ - return self.rels.add_relationship(reltype, target, rId, is_external) - - @property - def main_document_part(self): - """ - Return a reference to the main document part for this package. - Examples include a document part for a WordprocessingML package, a - presentation part for a PresentationML package, or a workbook part - for a SpreadsheetML package. - """ - return self.part_related_by(RT.OFFICE_DOCUMENT) - - @classmethod - def open(cls, pkg_file): - """ - Return an |OpcPackage| instance loaded with the contents of - *pkg_file*. - """ - pkg_reader = PackageReader.from_file(pkg_file) - package = cls() - Unmarshaller.unmarshal(pkg_reader, package, PartFactory) - return package - - def part_related_by(self, reltype): - """ - Return part to which this package has a relationship of *reltype*. - Raises |KeyError| if no such relationship is found and |ValueError| - if more than one such relationship is found. - """ - return self.rels.part_with_reltype(reltype) - - @property - def parts(self): - """ - Return a list containing a reference to each of the parts in this - package. - """ - return [part for part in self.iter_parts()] - - def relate_to(self, part, reltype): - """ - Return rId key of relationship to *part*, from the existing - relationship if there is one, otherwise a newly created one. - """ - rel = self.rels.get_or_add(reltype, part) - return rel.rId - - @lazyproperty - def rels(self): - """ - Return a reference to the |Relationships| instance holding the - collection of relationships for this package. - """ - return Relationships(PACKAGE_URI.baseURI) - - def save(self, pkg_file): - """ - Save this package to *pkg_file*, where *file* can be either a path to - a file (a string) or a file-like object. - """ - for part in self.parts: - part.before_marshal() - PackageWriter.write(pkg_file, self.rels, self.parts) - - @property - def _core_properties_part(self): - """ - |CorePropertiesPart| object related to this package. Creates - a default core properties part if one is not present (not common). - """ - try: - return self.part_related_by(RT.CORE_PROPERTIES) - except KeyError: - core_properties_part = CorePropertiesPart.default(self) - self.relate_to(core_properties_part, RT.CORE_PROPERTIES) - return core_properties_part - - @property - def _comments_part(self): - """ - |CommentsPart| object related to this package. Creates - a default Comments part if one is not present. - """ - try: - return self.part_related_by(RT.COMMENTS) - except KeyError: - comments_part = CommentsPart.default(self) - self.relate_to(comments_part, RT.COMMENTS) - return comments_part - - @property - def _footnotes_part(self): - """ - |FootnotesPart| object related to this package. Creates - a default Comments part if one is not present. - """ - try: - return self.part_related_by(RT.FOOTNOTES) - except KeyError: - footnotes_part = FootnotesPart.default(self) - self.relate_to(footnotes_part, RT.FOOTNOTES) - return footnotes_part - - -class Unmarshaller(object): - """ - Hosts static methods for unmarshalling a package from a |PackageReader| - instance. - """ - @staticmethod - def unmarshal(pkg_reader, package, part_factory): - """ - Construct graph of parts and realized relationships based on the - contents of *pkg_reader*, delegating construction of each part to - *part_factory*. Package relationships are added to *pkg*. - """ - parts = Unmarshaller._unmarshal_parts( - pkg_reader, package, part_factory - ) - Unmarshaller._unmarshal_relationships(pkg_reader, package, parts) - for part in parts.values(): - part.after_unmarshal() - package.after_unmarshal() - - @staticmethod - def _unmarshal_parts(pkg_reader, package, part_factory): - """ - Return a dictionary of |Part| instances unmarshalled from - *pkg_reader*, keyed by partname. Side-effect is that each part in - *pkg_reader* is constructed using *part_factory*. - """ - parts = {} - for partname, content_type, reltype, blob in pkg_reader.iter_sparts(): - parts[partname] = part_factory( - partname, content_type, reltype, blob, package - ) - return parts - - @staticmethod - def _unmarshal_relationships(pkg_reader, package, parts): - """ - Add a relationship to the source object corresponding to each of the - relationships in *pkg_reader* with its target_part set to the actual - target part in *parts*. - """ - for source_uri, srel in pkg_reader.iter_srels(): - source = package if source_uri == '/' else parts[source_uri] - target = (srel.target_ref if srel.is_external - else parts[srel.target_partname]) - source.load_rel(srel.reltype, target, srel.rId, srel.is_external) diff --git a/build/lib/docx/opc/packuri.py b/build/lib/docx/opc/packuri.py deleted file mode 100644 index 621ed92e5..000000000 --- a/build/lib/docx/opc/packuri.py +++ /dev/null @@ -1,117 +0,0 @@ -# encoding: utf-8 - -""" -Provides the PackURI value type along with some useful known pack URI strings -such as PACKAGE_URI. -""" - -import posixpath -import re - - -class PackURI(str): - """ - Provides access to pack URI components such as the baseURI and the - filename slice. Behaves as |str| otherwise. - """ - _filename_re = re.compile('([a-zA-Z]+)([1-9][0-9]*)?') - - def __new__(cls, pack_uri_str): - if not pack_uri_str[0] == '/': - tmpl = "PackURI must begin with slash, got '%s'" - raise ValueError(tmpl % pack_uri_str) - return str.__new__(cls, pack_uri_str) - - @staticmethod - def from_rel_ref(baseURI, relative_ref): - """ - Return a |PackURI| instance containing the absolute pack URI formed by - translating *relative_ref* onto *baseURI*. - """ - joined_uri = posixpath.join(baseURI, relative_ref) - abs_uri = posixpath.abspath(joined_uri) - return PackURI(abs_uri) - - @property - def baseURI(self): - """ - The base URI of this pack URI, the directory portion, roughly - speaking. E.g. ``'/ppt/slides'`` for ``'/ppt/slides/slide1.xml'``. - For the package pseudo-partname '/', baseURI is '/'. - """ - return posixpath.split(self)[0] - - @property - def ext(self): - """ - The extension portion of this pack URI, e.g. ``'xml'`` for - ``'/word/document.xml'``. Note the period is not included. - """ - # raw_ext is either empty string or starts with period, e.g. '.xml' - raw_ext = posixpath.splitext(self)[1] - return raw_ext[1:] if raw_ext.startswith('.') else raw_ext - - @property - def filename(self): - """ - The "filename" portion of this pack URI, e.g. ``'slide1.xml'`` for - ``'/ppt/slides/slide1.xml'``. For the package pseudo-partname '/', - filename is ''. - """ - return posixpath.split(self)[1] - - @property - def idx(self): - """ - Return partname index as integer for tuple partname or None for - singleton partname, e.g. ``21`` for ``'/ppt/slides/slide21.xml'`` and - |None| for ``'/ppt/presentation.xml'``. - """ - filename = self.filename - if not filename: - return None - name_part = posixpath.splitext(filename)[0] # filename w/ext removed - match = self._filename_re.match(name_part) - if match is None: - return None - if match.group(2): - return int(match.group(2)) - return None - - @property - def membername(self): - """ - The pack URI with the leading slash stripped off, the form used as - the Zip file membername for the package item. Returns '' for the - package pseudo-partname '/'. - """ - return self[1:] - - def relative_ref(self, baseURI): - """ - Return string containing relative reference to package item from - *baseURI*. E.g. PackURI('/ppt/slideLayouts/slideLayout1.xml') would - return '../slideLayouts/slideLayout1.xml' for baseURI '/ppt/slides'. - """ - # workaround for posixpath bug in 2.6, doesn't generate correct - # relative path when *start* (second) parameter is root ('/') - if baseURI == '/': - relpath = self[1:] - else: - relpath = posixpath.relpath(self, baseURI) - return relpath - - @property - def rels_uri(self): - """ - The pack URI of the .rels part corresponding to the current pack URI. - Only produces sensible output if the pack URI is a partname or the - package pseudo-partname '/'. - """ - rels_filename = '%s.rels' % self.filename - rels_uri_str = posixpath.join(self.baseURI, '_rels', rels_filename) - return PackURI(rels_uri_str) - - -PACKAGE_URI = PackURI('/') -CONTENT_TYPES_URI = PackURI('/[Content_Types].xml') diff --git a/build/lib/docx/opc/part.py b/build/lib/docx/opc/part.py deleted file mode 100644 index 928d3c183..000000000 --- a/build/lib/docx/opc/part.py +++ /dev/null @@ -1,241 +0,0 @@ -# encoding: utf-8 - -""" -Open Packaging Convention (OPC) objects related to package parts. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from .compat import cls_method_fn -from .oxml import serialize_part_xml -from ..oxml import parse_xml -from .packuri import PackURI -from .rel import Relationships -from .shared import lazyproperty - - -class Part(object): - """ - Base class for package parts. Provides common properties and methods, but - intended to be subclassed in client code to implement specific part - behaviors. - """ - def __init__(self, partname, content_type, blob=None, package=None): - super(Part, self).__init__() - self._partname = partname - self._content_type = content_type - self._blob = blob - self._package = package - - def after_unmarshal(self): - """ - Entry point for post-unmarshaling processing, for example to parse - the part XML. May be overridden by subclasses without forwarding call - to super. - """ - # don't place any code here, just catch call if not overridden by - # subclass - pass - - def before_marshal(self): - """ - Entry point for pre-serialization processing, for example to finalize - part naming if necessary. May be overridden by subclasses without - forwarding call to super. - """ - # don't place any code here, just catch call if not overridden by - # subclass - pass - - @property - def blob(self): - """ - Contents of this package part as a sequence of bytes. May be text or - binary. Intended to be overridden by subclasses. Default behavior is - to return load blob. - """ - return self._blob - - @property - def content_type(self): - """ - Content type of this part. - """ - return self._content_type - - def drop_rel(self, rId): - """ - Remove the relationship identified by *rId* if its reference count - is less than 2. Relationships with a reference count of 0 are - implicit relationships. - """ - if self._rel_ref_count(rId) < 2: - del self.rels[rId] - - @classmethod - def load(cls, partname, content_type, blob, package): - return cls(partname, content_type, blob, package) - - def load_rel(self, reltype, target, rId, is_external=False): - """ - Return newly added |_Relationship| instance of *reltype* between this - part and *target* with key *rId*. Target mode is set to - ``RTM.EXTERNAL`` if *is_external* is |True|. Intended for use during - load from a serialized package, where the rId is well-known. Other - methods exist for adding a new relationship to a part when - manipulating a part. - """ - return self.rels.add_relationship(reltype, target, rId, is_external) - - @property - def package(self): - """ - |OpcPackage| instance this part belongs to. - """ - return self._package - - @property - def partname(self): - """ - |PackURI| instance holding partname of this part, e.g. - '/ppt/slides/slide1.xml' - """ - return self._partname - - @partname.setter - def partname(self, partname): - if not isinstance(partname, PackURI): - tmpl = "partname must be instance of PackURI, got '%s'" - raise TypeError(tmpl % type(partname).__name__) - self._partname = partname - - def part_related_by(self, reltype): - """ - Return part to which this part has a relationship of *reltype*. - Raises |KeyError| if no such relationship is found and |ValueError| - if more than one such relationship is found. Provides ability to - resolve implicitly related part, such as Slide -> SlideLayout. - """ - return self.rels.part_with_reltype(reltype) - - def relate_to(self, target, reltype, is_external=False): - """ - Return rId key of relationship of *reltype* to *target*, from an - existing relationship if there is one, otherwise a newly created one. - """ - if is_external: - return self.rels.get_or_add_ext_rel(reltype, target) - else: - rel = self.rels.get_or_add(reltype, target) - return rel.rId - - @property - def related_parts(self): - """ - Dictionary mapping related parts by rId, so child objects can resolve - explicit relationships present in the part XML, e.g. sldIdLst to a - specific |Slide| instance. - """ - return self.rels.related_parts - - @lazyproperty - def rels(self): - """ - |Relationships| instance holding the relationships for this part. - """ - return Relationships(self._partname.baseURI) - - def target_ref(self, rId): - """ - Return URL contained in target ref of relationship identified by - *rId*. - """ - rel = self.rels[rId] - return rel.target_ref - - def _rel_ref_count(self, rId): - """ - Return the count of references in this part's XML to the relationship - identified by *rId*. - """ - rIds = self._element.xpath('//@r:id') - return len([_rId for _rId in rIds if _rId == rId]) - - -class PartFactory(object): - """ - Provides a way for client code to specify a subclass of |Part| to be - constructed by |Unmarshaller| based on its content type and/or a custom - callable. Setting ``PartFactory.part_class_selector`` to a callable - object will cause that object to be called with the parameters - ``content_type, reltype``, once for each part in the package. If the - callable returns an object, it is used as the class for that part. If it - returns |None|, part class selection falls back to the content type map - defined in ``PartFactory.part_type_for``. If no class is returned from - either of these, the class contained in ``PartFactory.default_part_type`` - is used to construct the part, which is by default ``opc.package.Part``. - """ - part_class_selector = None - part_type_for = {} - default_part_type = Part - - def __new__(cls, partname, content_type, reltype, blob, package): - PartClass = None - if cls.part_class_selector is not None: - part_class_selector = cls_method_fn(cls, 'part_class_selector') - PartClass = part_class_selector(content_type, reltype) - if PartClass is None: - PartClass = cls._part_cls_for(content_type) - return PartClass.load(partname, content_type, blob, package) - - @classmethod - def _part_cls_for(cls, content_type): - """ - Return the custom part class registered for *content_type*, or the - default part class if no custom class is registered for - *content_type*. - """ - if content_type in cls.part_type_for: - return cls.part_type_for[content_type] - return cls.default_part_type - - -class XmlPart(Part): - """ - Base class for package parts containing an XML payload, which is most of - them. Provides additional methods to the |Part| base class that take care - of parsing and reserializing the XML payload and managing relationships - to other parts. - """ - def __init__(self, partname, content_type, element, package): - super(XmlPart, self).__init__( - partname, content_type, package=package - ) - self._element = element - - @property - def blob(self): - return serialize_part_xml(self._element) - - @property - def element(self): - """ - The root XML element of this XML part. - """ - return self._element - - @classmethod - def load(cls, partname, content_type, blob, package): - element = parse_xml(blob) - return cls(partname, content_type, element, package) - - @property - def part(self): - """ - Part of the parent protocol, "children" of the document will not know - the part that contains them so must ask their parent object. That - chain of delegation ends here for child objects. - """ - return self diff --git a/build/lib/docx/opc/parts/__init__.py b/build/lib/docx/opc/parts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/lib/docx/opc/parts/coreprops.py b/build/lib/docx/opc/parts/coreprops.py deleted file mode 100644 index 3c692fb99..000000000 --- a/build/lib/docx/opc/parts/coreprops.py +++ /dev/null @@ -1,54 +0,0 @@ -# encoding: utf-8 - -""" -Core properties part, corresponds to ``/docProps/core.xml`` part in package. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from datetime import datetime - -from ..constants import CONTENT_TYPE as CT -from ..coreprops import CoreProperties -from ...oxml.coreprops import CT_CoreProperties -from ..packuri import PackURI -from ..part import XmlPart - - -class CorePropertiesPart(XmlPart): - """ - Corresponds to part named ``/docProps/core.xml``, containing the core - document properties for this document package. - """ - @classmethod - def default(cls, package): - """ - Return a new |CorePropertiesPart| object initialized with default - values for its base properties. - """ - core_properties_part = cls._new(package) - core_properties = core_properties_part.core_properties - core_properties.title = 'Word Document' - core_properties.last_modified_by = 'python-docx' - core_properties.revision = 1 - core_properties.modified = datetime.utcnow() - return core_properties_part - - @property - def core_properties(self): - """ - A |CoreProperties| object providing read/write access to the core - properties contained in this core properties part. - """ - return CoreProperties(self.element) - - @classmethod - def _new(cls, package): - partname = PackURI('/docProps/core.xml') - content_type = CT.OPC_CORE_PROPERTIES - coreProperties = CT_CoreProperties.new() - return CorePropertiesPart( - partname, content_type, coreProperties, package - ) diff --git a/build/lib/docx/opc/phys_pkg.py b/build/lib/docx/opc/phys_pkg.py deleted file mode 100644 index c86a51994..000000000 --- a/build/lib/docx/opc/phys_pkg.py +++ /dev/null @@ -1,155 +0,0 @@ -# encoding: utf-8 - -""" -Provides a general interface to a *physical* OPC package, such as a zip file. -""" - -from __future__ import absolute_import - -import os - -from zipfile import ZipFile, is_zipfile, ZIP_DEFLATED - -from .compat import is_string -from .exceptions import PackageNotFoundError -from .packuri import CONTENT_TYPES_URI - - -class PhysPkgReader(object): - """ - Factory for physical package reader objects. - """ - def __new__(cls, pkg_file): - # if *pkg_file* is a string, treat it as a path - if is_string(pkg_file): - if os.path.isdir(pkg_file): - reader_cls = _DirPkgReader - elif is_zipfile(pkg_file): - reader_cls = _ZipPkgReader - else: - raise PackageNotFoundError( - "Package not found at '%s'" % pkg_file - ) - else: # assume it's a stream and pass it to Zip reader to sort out - reader_cls = _ZipPkgReader - - return super(PhysPkgReader, cls).__new__(reader_cls) - - -class PhysPkgWriter(object): - """ - Factory for physical package writer objects. - """ - def __new__(cls, pkg_file): - return super(PhysPkgWriter, cls).__new__(_ZipPkgWriter) - - -class _DirPkgReader(PhysPkgReader): - """ - Implements |PhysPkgReader| interface for an OPC package extracted into a - directory. - """ - def __init__(self, path): - """ - *path* is the path to a directory containing an expanded package. - """ - super(_DirPkgReader, self).__init__() - self._path = os.path.abspath(path) - - def blob_for(self, pack_uri): - """ - Return contents of file corresponding to *pack_uri* in package - directory. - """ - path = os.path.join(self._path, pack_uri.membername) - with open(path, 'rb') as f: - blob = f.read() - return blob - - def close(self): - """ - Provides interface consistency with |ZipFileSystem|, but does - nothing, a directory file system doesn't need closing. - """ - pass - - @property - def content_types_xml(self): - """ - Return the `[Content_Types].xml` blob from the package. - """ - return self.blob_for(CONTENT_TYPES_URI) - - def rels_xml_for(self, source_uri): - """ - Return rels item XML for source with *source_uri*, or None if the - item has no rels item. - """ - try: - rels_xml = self.blob_for(source_uri.rels_uri) - except IOError: - rels_xml = None - return rels_xml - - -class _ZipPkgReader(PhysPkgReader): - """ - Implements |PhysPkgReader| interface for a zip file OPC package. - """ - def __init__(self, pkg_file): - super(_ZipPkgReader, self).__init__() - self._zipf = ZipFile(pkg_file, 'r') - - def blob_for(self, pack_uri): - """ - Return blob corresponding to *pack_uri*. Raises |ValueError| if no - matching member is present in zip archive. - """ - return self._zipf.read(pack_uri.membername) - - def close(self): - """ - Close the zip archive, releasing any resources it is using. - """ - self._zipf.close() - - @property - def content_types_xml(self): - """ - Return the `[Content_Types].xml` blob from the zip package. - """ - return self.blob_for(CONTENT_TYPES_URI) - - def rels_xml_for(self, source_uri): - """ - Return rels item XML for source with *source_uri* or None if no rels - item is present. - """ - try: - rels_xml = self.blob_for(source_uri.rels_uri) - except KeyError: - rels_xml = None - return rels_xml - - -class _ZipPkgWriter(PhysPkgWriter): - """ - Implements |PhysPkgWriter| interface for a zip file OPC package. - """ - def __init__(self, pkg_file): - super(_ZipPkgWriter, self).__init__() - self._zipf = ZipFile(pkg_file, 'w', compression=ZIP_DEFLATED) - - def close(self): - """ - Close the zip archive, flushing any pending physical writes and - releasing any resources it's using. - """ - self._zipf.close() - - def write(self, pack_uri, blob): - """ - Write *blob* to this zip package with the membername corresponding to - *pack_uri*. - """ - self._zipf.writestr(pack_uri.membername, blob) diff --git a/build/lib/docx/opc/pkgreader.py b/build/lib/docx/opc/pkgreader.py deleted file mode 100644 index ae80b3586..000000000 --- a/build/lib/docx/opc/pkgreader.py +++ /dev/null @@ -1,298 +0,0 @@ -# encoding: utf-8 - -""" -Provides a low-level, read-only API to a serialized Open Packaging Convention -(OPC) package. -""" - -from __future__ import absolute_import - -from .constants import RELATIONSHIP_TARGET_MODE as RTM -from .oxml import parse_xml -from .packuri import PACKAGE_URI, PackURI -from .phys_pkg import PhysPkgReader -from .shared import CaseInsensitiveDict - - -class PackageReader(object): - """ - Provides access to the contents of a zip-format OPC package via its - :attr:`serialized_parts` and :attr:`pkg_srels` attributes. - """ - def __init__(self, content_types, pkg_srels, sparts): - super(PackageReader, self).__init__() - self._pkg_srels = pkg_srels - self._sparts = sparts - - @staticmethod - def from_file(pkg_file): - """ - Return a |PackageReader| instance loaded with contents of *pkg_file*. - """ - phys_reader = PhysPkgReader(pkg_file) - content_types = _ContentTypeMap.from_xml(phys_reader.content_types_xml) - pkg_srels = PackageReader._srels_for(phys_reader, PACKAGE_URI) - sparts = PackageReader._load_serialized_parts( - phys_reader, pkg_srels, content_types - ) - phys_reader.close() - return PackageReader(content_types, pkg_srels, sparts) - - def iter_sparts(self): - """ - Generate a 4-tuple `(partname, content_type, reltype, blob)` for each - of the serialized parts in the package. - """ - for s in self._sparts: - yield (s.partname, s.content_type, s.reltype, s.blob) - - def iter_srels(self): - """ - Generate a 2-tuple `(source_uri, srel)` for each of the relationships - in the package. - """ - for srel in self._pkg_srels: - yield (PACKAGE_URI, srel) - for spart in self._sparts: - for srel in spart.srels: - yield (spart.partname, srel) - - @staticmethod - def _load_serialized_parts(phys_reader, pkg_srels, content_types): - """ - Return a list of |_SerializedPart| instances corresponding to the - parts in *phys_reader* accessible by walking the relationship graph - starting with *pkg_srels*. - """ - sparts = [] - part_walker = PackageReader._walk_phys_parts(phys_reader, pkg_srels) - for partname, blob, reltype, srels in part_walker: - content_type = content_types[partname] - spart = _SerializedPart( - partname, content_type, reltype, blob, srels - ) - sparts.append(spart) - return tuple(sparts) - - @staticmethod - def _srels_for(phys_reader, source_uri): - """ - Return |_SerializedRelationships| instance populated with - relationships for source identified by *source_uri*. - """ - rels_xml = phys_reader.rels_xml_for(source_uri) - return _SerializedRelationships.load_from_xml( - source_uri.baseURI, rels_xml) - - @staticmethod - def _walk_phys_parts(phys_reader, srels, visited_partnames=None): - """ - Generate a 4-tuple `(partname, blob, reltype, srels)` for each of the - parts in *phys_reader* by walking the relationship graph rooted at - srels. - """ - if visited_partnames is None: - visited_partnames = [] - for srel in srels: - if srel.is_external: - continue - partname = srel.target_partname - if partname in visited_partnames: - continue - visited_partnames.append(partname) - reltype = srel.reltype - part_srels = PackageReader._srels_for(phys_reader, partname) - blob = phys_reader.blob_for(partname) - yield (partname, blob, reltype, part_srels) - next_walker = PackageReader._walk_phys_parts( - phys_reader, part_srels, visited_partnames - ) - for partname, blob, reltype, srels in next_walker: - yield (partname, blob, reltype, srels) - - -class _ContentTypeMap(object): - """ - Value type providing dictionary semantics for looking up content type by - part name, e.g. ``content_type = cti['/ppt/presentation.xml']``. - """ - def __init__(self): - super(_ContentTypeMap, self).__init__() - self._overrides = CaseInsensitiveDict() - self._defaults = CaseInsensitiveDict() - - def __getitem__(self, partname): - """ - Return content type for part identified by *partname*. - """ - if not isinstance(partname, PackURI): - tmpl = "_ContentTypeMap key must be , got %s" - raise KeyError(tmpl % type(partname)) - if partname in self._overrides: - return self._overrides[partname] - if partname.ext in self._defaults: - return self._defaults[partname.ext] - tmpl = "no content type for partname '%s' in [Content_Types].xml" - raise KeyError(tmpl % partname) - - @staticmethod - def from_xml(content_types_xml): - """ - Return a new |_ContentTypeMap| instance populated with the contents - of *content_types_xml*. - """ - types_elm = parse_xml(content_types_xml) - ct_map = _ContentTypeMap() - for o in types_elm.overrides: - ct_map._add_override(o.partname, o.content_type) - for d in types_elm.defaults: - ct_map._add_default(d.extension, d.content_type) - return ct_map - - def _add_default(self, extension, content_type): - """ - Add the default mapping of *extension* to *content_type* to this - content type mapping. - """ - self._defaults[extension] = content_type - - def _add_override(self, partname, content_type): - """ - Add the default mapping of *partname* to *content_type* to this - content type mapping. - """ - self._overrides[partname] = content_type - - -class _SerializedPart(object): - """ - Value object for an OPC package part. Provides access to the partname, - content type, blob, and serialized relationships for the part. - """ - def __init__(self, partname, content_type, reltype, blob, srels): - super(_SerializedPart, self).__init__() - self._partname = partname - self._content_type = content_type - self._reltype = reltype - self._blob = blob - self._srels = srels - - @property - def partname(self): - return self._partname - - @property - def content_type(self): - return self._content_type - - @property - def blob(self): - return self._blob - - @property - def reltype(self): - """ - The referring relationship type of this part. - """ - return self._reltype - - @property - def srels(self): - return self._srels - - -class _SerializedRelationship(object): - """ - Value object representing a serialized relationship in an OPC package. - Serialized, in this case, means any target part is referred to via its - partname rather than a direct link to an in-memory |Part| object. - """ - def __init__(self, baseURI, rel_elm): - super(_SerializedRelationship, self).__init__() - self._baseURI = baseURI - self._rId = rel_elm.rId - self._reltype = rel_elm.reltype - self._target_mode = rel_elm.target_mode - self._target_ref = rel_elm.target_ref - - @property - def is_external(self): - """ - True if target_mode is ``RTM.EXTERNAL`` - """ - return self._target_mode == RTM.EXTERNAL - - @property - def reltype(self): - """Relationship type, like ``RT.OFFICE_DOCUMENT``""" - return self._reltype - - @property - def rId(self): - """ - Relationship id, like 'rId9', corresponds to the ``Id`` attribute on - the ``CT_Relationship`` element. - """ - return self._rId - - @property - def target_mode(self): - """ - String in ``TargetMode`` attribute of ``CT_Relationship`` element, - one of ``RTM.INTERNAL`` or ``RTM.EXTERNAL``. - """ - return self._target_mode - - @property - def target_ref(self): - """ - String in ``Target`` attribute of ``CT_Relationship`` element, a - relative part reference for internal target mode or an arbitrary URI, - e.g. an HTTP URL, for external target mode. - """ - return self._target_ref - - @property - def target_partname(self): - """ - |PackURI| instance containing partname targeted by this relationship. - Raises ``ValueError`` on reference if target_mode is ``'External'``. - Use :attr:`target_mode` to check before referencing. - """ - if self.is_external: - msg = ('target_partname attribute on Relationship is undefined w' - 'here TargetMode == "External"') - raise ValueError(msg) - # lazy-load _target_partname attribute - if not hasattr(self, '_target_partname'): - self._target_partname = PackURI.from_rel_ref(self._baseURI, - self.target_ref) - return self._target_partname - - -class _SerializedRelationships(object): - """ - Read-only sequence of |_SerializedRelationship| instances corresponding - to the relationships item XML passed to constructor. - """ - def __init__(self): - super(_SerializedRelationships, self).__init__() - self._srels = [] - - def __iter__(self): - """Support iteration, e.g. 'for x in srels:'""" - return self._srels.__iter__() - - @staticmethod - def load_from_xml(baseURI, rels_item_xml): - """ - Return |_SerializedRelationships| instance loaded with the - relationships contained in *rels_item_xml*. Returns an empty - collection if *rels_item_xml* is |None|. - """ - srels = _SerializedRelationships() - if rels_item_xml is not None: - rels_elm = parse_xml(rels_item_xml) - for rel_elm in rels_elm.Relationship_lst: - srels._srels.append(_SerializedRelationship(baseURI, rel_elm)) - return srels diff --git a/build/lib/docx/opc/pkgwriter.py b/build/lib/docx/opc/pkgwriter.py deleted file mode 100644 index fccda6cd8..000000000 --- a/build/lib/docx/opc/pkgwriter.py +++ /dev/null @@ -1,125 +0,0 @@ -# encoding: utf-8 - -""" -Provides a low-level, write-only API to a serialized Open Packaging -Convention (OPC) package, essentially an implementation of OpcPackage.save() -""" - -from __future__ import absolute_import - -from .constants import CONTENT_TYPE as CT -from .oxml import CT_Types, serialize_part_xml -from .packuri import CONTENT_TYPES_URI, PACKAGE_URI -from .phys_pkg import PhysPkgWriter -from .shared import CaseInsensitiveDict -from .spec import default_content_types - - -class PackageWriter(object): - """ - Writes a zip-format OPC package to *pkg_file*, where *pkg_file* can be - either a path to a zip file (a string) or a file-like object. Its single - API method, :meth:`write`, is static, so this class is not intended to - be instantiated. - """ - @staticmethod - def write(pkg_file, pkg_rels, parts): - """ - Write a physical package (.pptx file) to *pkg_file* containing - *pkg_rels* and *parts* and a content types stream based on the - content types of the parts. - """ - phys_writer = PhysPkgWriter(pkg_file) - PackageWriter._write_content_types_stream(phys_writer, parts) - PackageWriter._write_pkg_rels(phys_writer, pkg_rels) - PackageWriter._write_parts(phys_writer, parts) - phys_writer.close() - - @staticmethod - def _write_content_types_stream(phys_writer, parts): - """ - Write ``[Content_Types].xml`` part to the physical package with an - appropriate content type lookup target for each part in *parts*. - """ - cti = _ContentTypesItem.from_parts(parts) - phys_writer.write(CONTENT_TYPES_URI, cti.blob) - - @staticmethod - def _write_parts(phys_writer, parts): - """ - Write the blob of each part in *parts* to the package, along with a - rels item for its relationships if and only if it has any. - """ - for part in parts: - phys_writer.write(part.partname, part.blob) - if len(part._rels): - phys_writer.write(part.partname.rels_uri, part._rels.xml) - - @staticmethod - def _write_pkg_rels(phys_writer, pkg_rels): - """ - Write the XML rels item for *pkg_rels* ('/_rels/.rels') to the - package. - """ - phys_writer.write(PACKAGE_URI.rels_uri, pkg_rels.xml) - - -class _ContentTypesItem(object): - """ - Service class that composes a content types item ([Content_Types].xml) - based on a list of parts. Not meant to be instantiated directly, its - single interface method is xml_for(), e.g. - ``_ContentTypesItem.xml_for(parts)``. - """ - def __init__(self): - self._defaults = CaseInsensitiveDict() - self._overrides = dict() - - @property - def blob(self): - """ - Return XML form of this content types item, suitable for storage as - ``[Content_Types].xml`` in an OPC package. - """ - return serialize_part_xml(self._element) - - @classmethod - def from_parts(cls, parts): - """ - Return content types XML mapping each part in *parts* to the - appropriate content type and suitable for storage as - ``[Content_Types].xml`` in an OPC package. - """ - cti = cls() - cti._defaults['rels'] = CT.OPC_RELATIONSHIPS - cti._defaults['xml'] = CT.XML - for part in parts: - cti._add_content_type(part.partname, part.content_type) - return cti - - def _add_content_type(self, partname, content_type): - """ - Add a content type for the part with *partname* and *content_type*, - using a default or override as appropriate. - """ - ext = partname.ext - if (ext.lower(), content_type) in default_content_types: - self._defaults[ext] = content_type - else: - self._overrides[partname] = content_type - - @property - def _element(self): - """ - Return XML form of this content types item, suitable for storage as - ``[Content_Types].xml`` in an OPC package. Although the sequence of - elements is not strictly significant, as an aid to testing and - readability Default elements are sorted by extension and Override - elements are sorted by partname. - """ - _types_elm = CT_Types.new() - for ext in sorted(self._defaults.keys()): - _types_elm.add_default(ext, self._defaults[ext]) - for partname in sorted(self._overrides.keys()): - _types_elm.add_override(partname, self._overrides[partname]) - return _types_elm diff --git a/build/lib/docx/opc/rel.py b/build/lib/docx/opc/rel.py deleted file mode 100644 index 7dba2af8e..000000000 --- a/build/lib/docx/opc/rel.py +++ /dev/null @@ -1,170 +0,0 @@ -# encoding: utf-8 - -""" -Relationship-related objects. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from .oxml import CT_Relationships - - -class Relationships(dict): - """ - Collection object for |_Relationship| instances, having list semantics. - """ - def __init__(self, baseURI): - super(Relationships, self).__init__() - self._baseURI = baseURI - self._target_parts_by_rId = {} - - def add_relationship(self, reltype, target, rId, is_external=False): - """ - Return a newly added |_Relationship| instance. - """ - rel = _Relationship(rId, reltype, target, self._baseURI, is_external) - self[rId] = rel - if not is_external: - self._target_parts_by_rId[rId] = target - return rel - - def get_or_add(self, reltype, target_part): - """ - Return relationship of *reltype* to *target_part*, newly added if not - already present in collection. - """ - rel = self._get_matching(reltype, target_part) - if rel is None: - rId = self._next_rId - rel = self.add_relationship(reltype, target_part, rId) - return rel - - def get_or_add_ext_rel(self, reltype, target_ref): - """ - Return rId of external relationship of *reltype* to *target_ref*, - newly added if not already present in collection. - """ - rel = self._get_matching(reltype, target_ref, is_external=True) - if rel is None: - rId = self._next_rId - rel = self.add_relationship( - reltype, target_ref, rId, is_external=True - ) - return rel.rId - - def part_with_reltype(self, reltype): - """ - Return target part of rel with matching *reltype*, raising |KeyError| - if not found and |ValueError| if more than one matching relationship - is found. - """ - rel = self._get_rel_of_type(reltype) - return rel.target_part - - @property - def related_parts(self): - """ - dict mapping rIds to target parts for all the internal relationships - in the collection. - """ - return self._target_parts_by_rId - - @property - def xml(self): - """ - Serialize this relationship collection into XML suitable for storage - as a .rels file in an OPC package. - """ - rels_elm = CT_Relationships.new() - for rel in self.values(): - rels_elm.add_rel( - rel.rId, rel.reltype, rel.target_ref, rel.is_external - ) - return rels_elm.xml - - def _get_matching(self, reltype, target, is_external=False): - """ - Return relationship of matching *reltype*, *target*, and - *is_external* from collection, or None if not found. - """ - def matches(rel, reltype, target, is_external): - if rel.reltype != reltype: - return False - if rel.is_external != is_external: - return False - rel_target = rel.target_ref if rel.is_external else rel.target_part - if rel_target != target: - return False - return True - - for rel in self.values(): - if matches(rel, reltype, target, is_external): - return rel - return None - - def _get_rel_of_type(self, reltype): - """ - Return single relationship of type *reltype* from the collection. - Raises |KeyError| if no matching relationship is found. Raises - |ValueError| if more than one matching relationship is found. - """ - matching = [rel for rel in self.values() if rel.reltype == reltype] - if len(matching) == 0: - tmpl = "no relationship of type '%s' in collection" - raise KeyError(tmpl % reltype) - if len(matching) > 1: - tmpl = "multiple relationships of type '%s' in collection" - raise ValueError(tmpl % reltype) - return matching[0] - - @property - def _next_rId(self): - """ - Next available rId in collection, starting from 'rId1' and making use - of any gaps in numbering, e.g. 'rId2' for rIds ['rId1', 'rId3']. - """ - for n in range(1, len(self)+2): - rId_candidate = 'rId%d' % n # like 'rId19' - if rId_candidate not in self: - return rId_candidate - - -class _Relationship(object): - """ - Value object for relationship to part. - """ - def __init__(self, rId, reltype, target, baseURI, external=False): - super(_Relationship, self).__init__() - self._rId = rId - self._reltype = reltype - self._target = target - self._baseURI = baseURI - self._is_external = bool(external) - - @property - def is_external(self): - return self._is_external - - @property - def reltype(self): - return self._reltype - - @property - def rId(self): - return self._rId - - @property - def target_part(self): - if self._is_external: - raise ValueError("target_part property on _Relationship is undef" - "ined when target mode is External") - return self._target - - @property - def target_ref(self): - if self._is_external: - return self._target - else: - return self._target.partname.relative_ref(self._baseURI) diff --git a/build/lib/docx/opc/shared.py b/build/lib/docx/opc/shared.py deleted file mode 100644 index 55344483d..000000000 --- a/build/lib/docx/opc/shared.py +++ /dev/null @@ -1,47 +0,0 @@ -# encoding: utf-8 - -""" -Objects shared by opc modules. -""" - -from __future__ import absolute_import, print_function, unicode_literals - - -class CaseInsensitiveDict(dict): - """ - Mapping type that behaves like dict except that it matches without respect - to the case of the key. E.g. cid['A'] == cid['a']. Note this is not - general-purpose, just complete enough to satisfy opc package needs. It - assumes str keys, and that it is created empty; keys passed in constructor - are not accounted for - """ - def __contains__(self, key): - return super(CaseInsensitiveDict, self).__contains__(key.lower()) - - def __getitem__(self, key): - return super(CaseInsensitiveDict, self).__getitem__(key.lower()) - - def __setitem__(self, key, value): - return super(CaseInsensitiveDict, self).__setitem__( - key.lower(), value - ) - - -def lazyproperty(f): - """ - @lazyprop decorator. Decorated method will be called only on first access - to calculate a cached property value. After that, the cached value is - returned. - """ - cache_attr_name = '_%s' % f.__name__ # like '_foobar' for prop 'foobar' - docstring = f.__doc__ - - def get_prop_value(obj): - try: - return getattr(obj, cache_attr_name) - except AttributeError: - value = f(obj) - setattr(obj, cache_attr_name, value) - return value - - return property(get_prop_value, doc=docstring) diff --git a/build/lib/docx/opc/spec.py b/build/lib/docx/opc/spec.py deleted file mode 100644 index 60fc38564..000000000 --- a/build/lib/docx/opc/spec.py +++ /dev/null @@ -1,29 +0,0 @@ -# encoding: utf-8 - -""" -Provides mappings that embody aspects of the Open XML spec ISO/IEC 29500. -""" - -from .constants import CONTENT_TYPE as CT - - -default_content_types = ( - ('bin', CT.PML_PRINTER_SETTINGS), - ('bin', CT.SML_PRINTER_SETTINGS), - ('bin', CT.WML_PRINTER_SETTINGS), - ('bmp', CT.BMP), - ('emf', CT.X_EMF), - ('fntdata', CT.X_FONTDATA), - ('gif', CT.GIF), - ('jpe', CT.JPEG), - ('jpeg', CT.JPEG), - ('jpg', CT.JPEG), - ('png', CT.PNG), - ('rels', CT.OPC_RELATIONSHIPS), - ('tif', CT.TIFF), - ('tiff', CT.TIFF), - ('wdp', CT.MS_PHOTO), - ('wmf', CT.X_WMF), - ('xlsx', CT.SML_SHEET), - ('xml', CT.XML), -) diff --git a/build/lib/docx/oxml/__init__.py b/build/lib/docx/oxml/__init__.py deleted file mode 100644 index 83ca3ce40..000000000 --- a/build/lib/docx/oxml/__init__.py +++ /dev/null @@ -1,228 +0,0 @@ -# encoding: utf-8 - -""" -Initializes oxml sub-package, including registering custom element classes -corresponding to Open XML elements. -""" - -from __future__ import absolute_import - -from lxml import etree - -from .ns import NamespacePrefixedTag, nsmap - - -# configure XML parser -element_class_lookup = etree.ElementNamespaceClassLookup() -oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) -oxml_parser.set_element_class_lookup(element_class_lookup) - - -def parse_xml(xml): - """ - Return root lxml element obtained by parsing XML character string in - *xml*, which can be either a Python 2.x string or unicode. The custom - parser is used, so custom element classes are produced for elements in - *xml* that have them. - """ - root_element = etree.fromstring(xml, oxml_parser) - return root_element - - -def register_element_cls(tag, cls): - """ - Register *cls* to be constructed when the oxml parser encounters an - element with matching *tag*. *tag* is a string of the form - ``nspfx:tagroot``, e.g. ``'w:document'``. - """ - nspfx, tagroot = tag.split(':') - namespace = element_class_lookup.get_namespace(nsmap[nspfx]) - namespace[tagroot] = cls - - -def OxmlElement(nsptag_str, attrs=None, nsdecls=None): - """ - Return a 'loose' lxml element having the tag specified by *nsptag_str*. - *nsptag_str* must contain the standard namespace prefix, e.g. 'a:tbl'. - The resulting element is an instance of the custom element class for this - tag name if one is defined. A dictionary of attribute values may be - provided as *attrs*; they are set if present. All namespaces defined in - the dict *nsdecls* are declared in the element using the key as the - prefix and the value as the namespace name. If *nsdecls* is not provided, - a single namespace declaration is added based on the prefix on - *nsptag_str*. - """ - nsptag = NamespacePrefixedTag(nsptag_str) - if nsdecls is None: - nsdecls = nsptag.nsmap - return oxml_parser.makeelement( - nsptag.clark_name, attrib=attrs, nsmap=nsdecls - ) - - -# =========================================================================== -# custom element class mappings -# =========================================================================== - -from .shared import CT_DecimalNumber, CT_OnOff, CT_String - - -from .coreprops import CT_CoreProperties -register_element_cls('cp:coreProperties', CT_CoreProperties) - -from .document import CT_Body, CT_Document -register_element_cls('w:body', CT_Body) -register_element_cls('w:document', CT_Document) - -from .numbering import ( - CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr -) -register_element_cls('w:abstractNumId', CT_DecimalNumber) -register_element_cls('w:ilvl', CT_DecimalNumber) -register_element_cls('w:lvlOverride', CT_NumLvl) -register_element_cls('w:num', CT_Num) -register_element_cls('w:numId', CT_DecimalNumber) -register_element_cls('w:numPr', CT_NumPr) -register_element_cls('w:numbering', CT_Numbering) -register_element_cls('w:startOverride', CT_DecimalNumber) - -from .section import CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType -register_element_cls('w:pgMar', CT_PageMar) -register_element_cls('w:pgSz', CT_PageSz) -register_element_cls('w:sectPr', CT_SectPr) -register_element_cls('w:type', CT_SectType) - -from .shape import ( - CT_Blip, CT_BlipFillProperties, CT_GraphicalObject, - CT_GraphicalObjectData, CT_Inline, CT_NonVisualDrawingProps, CT_Picture, - CT_PictureNonVisual, CT_Point2D, CT_PositiveSize2D, CT_ShapeProperties, - CT_Transform2D -) -register_element_cls('a:blip', CT_Blip) -register_element_cls('a:ext', CT_PositiveSize2D) -register_element_cls('a:graphic', CT_GraphicalObject) -register_element_cls('a:graphicData', CT_GraphicalObjectData) -register_element_cls('a:off', CT_Point2D) -register_element_cls('a:xfrm', CT_Transform2D) -register_element_cls('pic:blipFill', CT_BlipFillProperties) -register_element_cls('pic:cNvPr', CT_NonVisualDrawingProps) -register_element_cls('pic:nvPicPr', CT_PictureNonVisual) -register_element_cls('pic:pic', CT_Picture) -register_element_cls('pic:spPr', CT_ShapeProperties) -register_element_cls('wp:docPr', CT_NonVisualDrawingProps) -register_element_cls('wp:extent', CT_PositiveSize2D) -register_element_cls('wp:inline', CT_Inline) - -from .styles import CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles -register_element_cls('w:basedOn', CT_String) -register_element_cls('w:latentStyles', CT_LatentStyles) -register_element_cls('w:locked', CT_OnOff) -register_element_cls('w:lsdException', CT_LsdException) -register_element_cls('w:name', CT_String) -register_element_cls('w:next', CT_String) -register_element_cls('w:qFormat', CT_OnOff) -register_element_cls('w:semiHidden', CT_OnOff) -register_element_cls('w:style', CT_Style) -register_element_cls('w:styles', CT_Styles) -register_element_cls('w:uiPriority', CT_DecimalNumber) -register_element_cls('w:unhideWhenUsed', CT_OnOff) - -from .table import ( - CT_Height, CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, - CT_TblPr, CT_TblWidth, CT_TblMar, CT_Tc, CT_TcPr, CT_TrPr, CT_VerticalJc, - CT_VMerge, CT_TblBoarders, CT_Bottom, CT_TcBorders -) -register_element_cls('w:bidiVisual', CT_OnOff) -register_element_cls('w:gridCol', CT_TblGridCol) -register_element_cls('w:gridSpan', CT_DecimalNumber) -register_element_cls('w:tbl', CT_Tbl) -register_element_cls('w:tblGrid', CT_TblGrid) -register_element_cls('w:tblLayout', CT_TblLayoutType) -register_element_cls('w:tblPr', CT_TblPr) -register_element_cls('w:tblW', CT_TblWidth) -register_element_cls('w:tblCellMar', CT_TblMar) -register_element_cls('w:tblStyle', CT_String) -register_element_cls('w:tc', CT_Tc) -register_element_cls('w:tcPr', CT_TcPr) -register_element_cls('w:tcW', CT_TblWidth) -register_element_cls('w:tr', CT_Row) -register_element_cls('w:trHeight', CT_Height) -register_element_cls('w:trPr', CT_TrPr) -register_element_cls('w:vAlign', CT_VerticalJc) -register_element_cls('w:vMerge', CT_VMerge) -register_element_cls('w:tblBorders', CT_TblBoarders) -register_element_cls('w:tcBorders', CT_TcBorders) -register_element_cls('w:bottom', CT_Bottom) - -from .text.font import ( - CT_Color, CT_Fonts, CT_Highlight, CT_HpsMeasure, CT_RPr, CT_Underline, - CT_VerticalAlignRun -) -register_element_cls('w:b', CT_OnOff) -register_element_cls('w:bCs', CT_OnOff) -register_element_cls('w:caps', CT_OnOff) -register_element_cls('w:color', CT_Color) -register_element_cls('w:cs', CT_OnOff) -register_element_cls('w:dstrike', CT_OnOff) -register_element_cls('w:emboss', CT_OnOff) -register_element_cls('w:highlight', CT_Highlight) -register_element_cls('w:i', CT_OnOff) -register_element_cls('w:iCs', CT_OnOff) -register_element_cls('w:imprint', CT_OnOff) -register_element_cls('w:noProof', CT_OnOff) -register_element_cls('w:oMath', CT_OnOff) -register_element_cls('w:outline', CT_OnOff) -register_element_cls('w:rFonts', CT_Fonts) -register_element_cls('w:rPr', CT_RPr) -register_element_cls('w:rStyle', CT_String) -register_element_cls('w:rtl', CT_OnOff) -register_element_cls('w:shadow', CT_OnOff) -register_element_cls('w:smallCaps', CT_OnOff) -register_element_cls('w:snapToGrid', CT_OnOff) -register_element_cls('w:specVanish', CT_OnOff) -register_element_cls('w:strike', CT_OnOff) -register_element_cls('w:sz', CT_HpsMeasure) -register_element_cls('w:u', CT_Underline) -register_element_cls('w:vanish', CT_OnOff) -register_element_cls('w:vertAlign', CT_VerticalAlignRun) -register_element_cls('w:webHidden', CT_OnOff) - -from .text.paragraph import CT_P -register_element_cls('w:p', CT_P) - -from .text.parfmt import ( - CT_Ind, CT_Jc, CT_PPr, CT_Spacing, CT_TabStop, CT_TabStops -) -register_element_cls('w:ind', CT_Ind) -register_element_cls('w:jc', CT_Jc) -register_element_cls('w:keepLines', CT_OnOff) -register_element_cls('w:keepNext', CT_OnOff) -register_element_cls('w:pageBreakBefore', CT_OnOff) -register_element_cls('w:pPr', CT_PPr) -register_element_cls('w:pStyle', CT_String) -register_element_cls('w:spacing', CT_Spacing) -register_element_cls('w:tab', CT_TabStop) -register_element_cls('w:tabs', CT_TabStops) -register_element_cls('w:widowControl', CT_OnOff) - -from .text.run import CT_Br, CT_R, CT_Text -register_element_cls('w:br', CT_Br) -register_element_cls('w:r', CT_R) -register_element_cls('w:t', CT_Text) -register_element_cls('w:rPr', CT_RPr) - - -from .comments import CT_Comments,CT_Com, CT_CRE, CT_CRS, CT_CRef -register_element_cls('w:comments', CT_Comments) -register_element_cls('w:comment', CT_Com) -register_element_cls('w:commentRangeStart', CT_CRS) -register_element_cls('w:commentRangeEnd', CT_CRE) -register_element_cls('w:commentReference', CT_CRef) - - -from .footnotes import CT_Footnotes, CT_Footnote, CT_FNR, CT_FootnoteRef - -register_element_cls('w:footnotes', CT_Footnotes) -register_element_cls('w:footnote', CT_Footnote) -register_element_cls('w:footnoteReference', CT_FNR) -register_element_cls('w:footnoteRef', CT_FootnoteRef) \ No newline at end of file diff --git a/build/lib/docx/oxml/comments.py b/build/lib/docx/oxml/comments.py deleted file mode 100644 index 49afde5ce..000000000 --- a/build/lib/docx/oxml/comments.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Custom element classes related to the comments part -""" - -from . import OxmlElement -from .simpletypes import ST_DecimalNumber, ST_String -from ..opc.constants import NAMESPACE -from ..text.paragraph import Paragraph -from ..text.run import Run -from .xmlchemy import ( - BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne -) - -class CT_Com(BaseOxmlElement): - """ - A ```` element, a container for Comment properties - """ - initials = RequiredAttribute('w:initials', ST_String) - _id = RequiredAttribute('w:id', ST_DecimalNumber) - date = RequiredAttribute('w:date', ST_String) - author = RequiredAttribute('w:author', ST_String) - - p = ZeroOrOne('w:p', successors=('w:comment',)) - - @classmethod - def new(cls, initials, comm_id, date, author): - """ - Return a new ```` element having _id of *comm_id* and having - the passed params as meta data - """ - comment = OxmlElement('w:comment') - comment.initials = initials - comment.date = date - comment._id = comm_id - comment.author = author - return comment - - def _add_p(self, text): - _p = OxmlElement('w:p') - _r = _p.add_r() - run = Run(_r,self) - run.text = text - self._insert_p(_p) - return _p - - @property - def meta(self): - return [self.author, self.initials, self.date] - - @property - def paragraph(self): - return Paragraph(self.p, self) - - - - -class CT_Comments(BaseOxmlElement): - """ - A ```` element, a container for Comments properties - """ - comment = ZeroOrMore ('w:comment', successors=('w:comments',)) - - def add_comment(self,author, initials, date): - _next_id = self._next_commentId - comment = CT_Com.new(initials, _next_id, date, author) - comment = self._insert_comment(comment) - - return comment - - @property - def _next_commentId(self): - ids = self.xpath('./w:comment/@w:id') - len(ids) - _ids = [int(_str) for _str in ids] - _ids.sort() - - try: - return _ids[-1] + 2 - except: - return 0 - - def get_comment_by_id(self, _id): - namesapce = NAMESPACE().WML_MAIN - for c in self.findall('.//w:comment',{'w':namesapce}): - if c._id == _id: - return c - return None - - -class CT_CRS(BaseOxmlElement): - """ - A ```` element - """ - _id = RequiredAttribute('w:id', ST_DecimalNumber) - - @classmethod - def new(cls, _id): - commentRangeStart = OxmlElement('w:commentRangeStart') - commentRangeStart._id =_id - - return commentRangeStart - -class CT_CRE(BaseOxmlElement): - """ - A ``w:commentRangeEnd`` element - """ - _id = RequiredAttribute('w:id', ST_DecimalNumber) - - - @classmethod - def new(cls, _id): - commentRangeEnd = OxmlElement('w:commentRangeEnd') - commentRangeEnd._id =_id - return commentRangeEnd - - -class CT_CRef(BaseOxmlElement): - """ - w:commentReference - """ - _id = RequiredAttribute('w:id', ST_DecimalNumber) - - @classmethod - def new (cls, _id): - commentReference = OxmlElement('w:commentReference') - commentReference._id =_id - return commentReference - - diff --git a/build/lib/docx/oxml/coreprops.py b/build/lib/docx/oxml/coreprops.py deleted file mode 100644 index 0ba85d027..000000000 --- a/build/lib/docx/oxml/coreprops.py +++ /dev/null @@ -1,317 +0,0 @@ -# encoding: utf-8 - -"""Custom element classes for core properties-related XML elements""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -import re - -from datetime import datetime, timedelta - -from docx.compat import is_string -from docx.oxml import parse_xml -from docx.oxml.ns import nsdecls, qn -from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne - - -class CT_CoreProperties(BaseOxmlElement): - """ - ```` element, the root element of the Core Properties - part stored as ``/docProps/core.xml``. Implements many of the Dublin Core - document metadata elements. String elements resolve to an empty string - ('') if the element is not present in the XML. String elements are - limited in length to 255 unicode characters. - """ - category = ZeroOrOne('cp:category', successors=()) - contentStatus = ZeroOrOne('cp:contentStatus', successors=()) - created = ZeroOrOne('dcterms:created', successors=()) - creator = ZeroOrOne('dc:creator', successors=()) - description = ZeroOrOne('dc:description', successors=()) - identifier = ZeroOrOne('dc:identifier', successors=()) - keywords = ZeroOrOne('cp:keywords', successors=()) - language = ZeroOrOne('dc:language', successors=()) - lastModifiedBy = ZeroOrOne('cp:lastModifiedBy', successors=()) - lastPrinted = ZeroOrOne('cp:lastPrinted', successors=()) - modified = ZeroOrOne('dcterms:modified', successors=()) - revision = ZeroOrOne('cp:revision', successors=()) - subject = ZeroOrOne('dc:subject', successors=()) - title = ZeroOrOne('dc:title', successors=()) - version = ZeroOrOne('cp:version', successors=()) - - _coreProperties_tmpl = ( - '\n' % nsdecls('cp', 'dc', 'dcterms') - ) - - @classmethod - def new(cls): - """ - Return a new ```` element - """ - xml = cls._coreProperties_tmpl - coreProperties = parse_xml(xml) - return coreProperties - - @property - def author_text(self): - """ - The text in the `dc:creator` child element. - """ - return self._text_of_element('creator') - - @author_text.setter - def author_text(self, value): - self._set_element_text('creator', value) - - @property - def category_text(self): - return self._text_of_element('category') - - @category_text.setter - def category_text(self, value): - self._set_element_text('category', value) - - @property - def comments_text(self): - return self._text_of_element('description') - - @comments_text.setter - def comments_text(self, value): - self._set_element_text('description', value) - - @property - def contentStatus_text(self): - return self._text_of_element('contentStatus') - - @contentStatus_text.setter - def contentStatus_text(self, value): - self._set_element_text('contentStatus', value) - - @property - def created_datetime(self): - return self._datetime_of_element('created') - - @created_datetime.setter - def created_datetime(self, value): - self._set_element_datetime('created', value) - - @property - def identifier_text(self): - return self._text_of_element('identifier') - - @identifier_text.setter - def identifier_text(self, value): - self._set_element_text('identifier', value) - - @property - def keywords_text(self): - return self._text_of_element('keywords') - - @keywords_text.setter - def keywords_text(self, value): - self._set_element_text('keywords', value) - - @property - def language_text(self): - return self._text_of_element('language') - - @language_text.setter - def language_text(self, value): - self._set_element_text('language', value) - - @property - def lastModifiedBy_text(self): - return self._text_of_element('lastModifiedBy') - - @lastModifiedBy_text.setter - def lastModifiedBy_text(self, value): - self._set_element_text('lastModifiedBy', value) - - @property - def lastPrinted_datetime(self): - return self._datetime_of_element('lastPrinted') - - @lastPrinted_datetime.setter - def lastPrinted_datetime(self, value): - self._set_element_datetime('lastPrinted', value) - - @property - def modified_datetime(self): - return self._datetime_of_element('modified') - - @modified_datetime.setter - def modified_datetime(self, value): - self._set_element_datetime('modified', value) - - @property - def revision_number(self): - """ - Integer value of revision property. - """ - revision = self.revision - if revision is None: - return 0 - revision_str = revision.text - try: - revision = int(revision_str) - except ValueError: - # non-integer revision strings also resolve to 0 - revision = 0 - # as do negative integers - if revision < 0: - revision = 0 - return revision - - @revision_number.setter - def revision_number(self, value): - """ - Set revision property to string value of integer *value*. - """ - if not isinstance(value, int) or value < 1: - tmpl = "revision property requires positive int, got '%s'" - raise ValueError(tmpl % value) - revision = self.get_or_add_revision() - revision.text = str(value) - - @property - def subject_text(self): - return self._text_of_element('subject') - - @subject_text.setter - def subject_text(self, value): - self._set_element_text('subject', value) - - @property - def title_text(self): - return self._text_of_element('title') - - @title_text.setter - def title_text(self, value): - self._set_element_text('title', value) - - @property - def version_text(self): - return self._text_of_element('version') - - @version_text.setter - def version_text(self, value): - self._set_element_text('version', value) - - def _datetime_of_element(self, property_name): - element = getattr(self, property_name) - if element is None: - return None - datetime_str = element.text - try: - return self._parse_W3CDTF_to_datetime(datetime_str) - except ValueError: - # invalid datetime strings are ignored - return None - - def _get_or_add(self, prop_name): - """ - Return element returned by 'get_or_add_' method for *prop_name*. - """ - get_or_add_method_name = 'get_or_add_%s' % prop_name - get_or_add_method = getattr(self, get_or_add_method_name) - element = get_or_add_method() - return element - - @classmethod - def _offset_dt(cls, dt, offset_str): - """ - Return a |datetime| instance that is offset from datetime *dt* by - the timezone offset specified in *offset_str*, a string like - ``'-07:00'``. - """ - match = cls._offset_pattern.match(offset_str) - if match is None: - raise ValueError( - "'%s' is not a valid offset string" % offset_str - ) - sign, hours_str, minutes_str = match.groups() - sign_factor = -1 if sign == '+' else 1 - hours = int(hours_str) * sign_factor - minutes = int(minutes_str) * sign_factor - td = timedelta(hours=hours, minutes=minutes) - return dt + td - - _offset_pattern = re.compile('([+-])(\d\d):(\d\d)') - - @classmethod - def _parse_W3CDTF_to_datetime(cls, w3cdtf_str): - # valid W3CDTF date cases: - # yyyy e.g. '2003' - # yyyy-mm e.g. '2003-12' - # yyyy-mm-dd e.g. '2003-12-31' - # UTC timezone e.g. '2003-12-31T10:14:55Z' - # numeric timezone e.g. '2003-12-31T10:14:55-08:00' - templates = ( - '%Y-%m-%dT%H:%M:%S', - '%Y-%m-%d', - '%Y-%m', - '%Y', - ) - # strptime isn't smart enough to parse literal timezone offsets like - # '-07:30', so we have to do it ourselves - parseable_part = w3cdtf_str[:19] - offset_str = w3cdtf_str[19:] - dt = None - for tmpl in templates: - try: - dt = datetime.strptime(parseable_part, tmpl) - except ValueError: - continue - if dt is None: - tmpl = "could not parse W3CDTF datetime string '%s'" - raise ValueError(tmpl % w3cdtf_str) - if len(offset_str) == 6: - return cls._offset_dt(dt, offset_str) - return dt - - def _set_element_datetime(self, prop_name, value): - """ - Set date/time value of child element having *prop_name* to *value*. - """ - if not isinstance(value, datetime): - tmpl = ( - "property requires object, got %s" - ) - raise ValueError(tmpl % type(value)) - element = self._get_or_add(prop_name) - dt_str = value.strftime('%Y-%m-%dT%H:%M:%SZ') - element.text = dt_str - if prop_name in ('created', 'modified'): - # These two require an explicit 'xsi:type="dcterms:W3CDTF"' - # attribute. The first and last line are a hack required to add - # the xsi namespace to the root element rather than each child - # element in which it is referenced - self.set(qn('xsi:foo'), 'bar') - element.set(qn('xsi:type'), 'dcterms:W3CDTF') - del self.attrib[qn('xsi:foo')] - - def _set_element_text(self, prop_name, value): - """Set string value of *name* property to *value*.""" - if not is_string(value): - value = str(value) - - if len(value) > 255: - tmpl = ( - "exceeded 255 char limit for property, got:\n\n'%s'" - ) - raise ValueError(tmpl % value) - element = self._get_or_add(prop_name) - element.text = value - - def _text_of_element(self, property_name): - """ - Return the text in the element matching *property_name*, or an empty - string if the element is not present or contains no text. - """ - element = getattr(self, property_name) - if element is None: - return '' - if element.text is None: - return '' - return element.text diff --git a/build/lib/docx/oxml/document.py b/build/lib/docx/oxml/document.py deleted file mode 100644 index e1cb4ac55..000000000 --- a/build/lib/docx/oxml/document.py +++ /dev/null @@ -1,59 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes that correspond to the document part, e.g. -. -""" - -from .xmlchemy import BaseOxmlElement, ZeroOrOne, ZeroOrMore - - -class CT_Document(BaseOxmlElement): - """ - ```` element, the root element of a document.xml file. - """ - body = ZeroOrOne('w:body') - - @property - def sectPr_lst(self): - """ - Return a list containing a reference to each ```` element - in the document, in the order encountered. - """ - return self.xpath('.//w:sectPr') - - -class CT_Body(BaseOxmlElement): - """ - ````, the container element for the main document story in - ``document.xml``. - """ - p = ZeroOrMore('w:p', successors=('w:sectPr',)) - tbl = ZeroOrMore('w:tbl', successors=('w:sectPr',)) - sectPr = ZeroOrOne('w:sectPr', successors=()) - - def add_section_break(self): - """ - Return the current ```` element after adding a clone of it - in a new ```` element appended to the block content elements. - Note that the "current" ```` will always be the sentinel - sectPr in this case since we're always working at the end of the - block content. - """ - sentinel_sectPr = self.get_or_add_sectPr() - cloned_sectPr = sentinel_sectPr.clone() - p = self.add_p() - p.set_sectPr(cloned_sectPr) - return sentinel_sectPr - - def clear_content(self): - """ - Remove all content child elements from this element. Leave - the element if it is present. - """ - if self.sectPr is not None: - content_elms = self[:-1] - else: - content_elms = self[:] - for content_elm in content_elms: - self.remove(content_elm) diff --git a/build/lib/docx/oxml/exceptions.py b/build/lib/docx/oxml/exceptions.py deleted file mode 100644 index 4696f1e93..000000000 --- a/build/lib/docx/oxml/exceptions.py +++ /dev/null @@ -1,16 +0,0 @@ -# encoding: utf-8 - -""" -Exceptions for oxml sub-package -""" - - -class XmlchemyError(Exception): - """Generic error class.""" - - -class InvalidXmlError(XmlchemyError): - """ - Raised when invalid XML is encountered, such as on attempt to access a - missing required child element - """ diff --git a/build/lib/docx/oxml/footnotes.py b/build/lib/docx/oxml/footnotes.py deleted file mode 100644 index 90c791bc3..000000000 --- a/build/lib/docx/oxml/footnotes.py +++ /dev/null @@ -1,89 +0,0 @@ -""" -Custom element classes related to the footnotes part -""" - - -from . import OxmlElement -from .simpletypes import ST_DecimalNumber, ST_String -from ..text.paragraph import Paragraph -from ..text.run import Run -from ..opc.constants import NAMESPACE -from .xmlchemy import ( - BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne -) - - -class CT_Footnotes(BaseOxmlElement): - """ - A ```` element, a container for Footnotes properties - """ - - footnote = ZeroOrMore ('w:footnote', successors=('w:footnotes',)) - - @property - def _next_id(self): - ids = self.xpath('./w:footnote/@w:id') - - return int(ids[-1]) + 1 - - def add_footnote(self): - _next_id = self._next_id - footnote = CT_Footnote.new(_next_id) - footnote = self._insert_footnote(footnote) - return footnote - - def get_footnote_by_id(self, _id): - namesapce = NAMESPACE().WML_MAIN - for fn in self.findall('.//w:footnote', {'w':namesapce}): - if fn._id == _id: - return fn - return None - -class CT_Footnote(BaseOxmlElement): - """ - A ```` element, a container for Footnote properties - """ - _id = RequiredAttribute('w:id', ST_DecimalNumber) - p = ZeroOrOne('w:p', successors=('w:footnote',)) - - @classmethod - def new(cls, _id): - footnote = OxmlElement('w:footnote') - footnote._id = _id - - return footnote - - def _add_p(self, text): - _p = OxmlElement('w:p') - _p.footnote_style() - - _r = _p.add_r() - _r.footnote_style() - _r = _p.add_r() - _r.add_footnoteRef() - - run = Run(_r, self) - run.text = text - - self._insert_p(_p) - return _p - - @property - def paragraph(self): - return Paragraph(self.p, self) - -class CT_FNR(BaseOxmlElement): - _id = RequiredAttribute('w:id', ST_DecimalNumber) - - @classmethod - def new (cls, _id): - footnoteReference = OxmlElement('w:footnoteReference') - footnoteReference._id = _id - return footnoteReference - -class CT_FootnoteRef (BaseOxmlElement): - - @classmethod - def new (cls): - ref = OxmlElement('w:footnoteRef') - return ref \ No newline at end of file diff --git a/build/lib/docx/oxml/ns.py b/build/lib/docx/oxml/ns.py deleted file mode 100644 index e6f6a4acc..000000000 --- a/build/lib/docx/oxml/ns.py +++ /dev/null @@ -1,114 +0,0 @@ -# encoding: utf-8 - -""" -Namespace-related objects. -""" - -from __future__ import absolute_import, print_function, unicode_literals - - -nsmap = { - 'a': ('http://schemas.openxmlformats.org/drawingml/2006/main'), - 'c': ('http://schemas.openxmlformats.org/drawingml/2006/chart'), - 'cp': ('http://schemas.openxmlformats.org/package/2006/metadata/core-pr' - 'operties'), - 'dc': ('http://purl.org/dc/elements/1.1/'), - 'dcmitype': ('http://purl.org/dc/dcmitype/'), - 'dcterms': ('http://purl.org/dc/terms/'), - 'dgm': ('http://schemas.openxmlformats.org/drawingml/2006/diagram'), - 'pic': ('http://schemas.openxmlformats.org/drawingml/2006/picture'), - 'r': ('http://schemas.openxmlformats.org/officeDocument/2006/relations' - 'hips'), - 'w': ('http://schemas.openxmlformats.org/wordprocessingml/2006/main'), - 'wp': ('http://schemas.openxmlformats.org/drawingml/2006/wordprocessing' - 'Drawing'), - 'xml': ('http://www.w3.org/XML/1998/namespace'), - 'xsi': ('http://www.w3.org/2001/XMLSchema-instance'), -} - -pfxmap = dict((value, key) for key, value in nsmap.items()) - - -class NamespacePrefixedTag(str): - """ - Value object that knows the semantics of an XML tag having a namespace - prefix. - """ - def __new__(cls, nstag, *args): - return super(NamespacePrefixedTag, cls).__new__(cls, nstag) - - def __init__(self, nstag): - self._pfx, self._local_part = nstag.split(':') - self._ns_uri = nsmap[self._pfx] - - @property - def clark_name(self): - return '{%s}%s' % (self._ns_uri, self._local_part) - - @classmethod - def from_clark_name(cls, clark_name): - nsuri, local_name = clark_name[1:].split('}') - nstag = '%s:%s' % (pfxmap[nsuri], local_name) - return cls(nstag) - - @property - def local_part(self): - """ - Return the local part of the tag as a string. E.g. 'foobar' is - returned for tag 'f:foobar'. - """ - return self._local_part - - @property - def nsmap(self): - """ - Return a dict having a single member, mapping the namespace prefix of - this tag to it's namespace name (e.g. {'f': 'http://foo/bar'}). This - is handy for passing to xpath calls and other uses. - """ - return {self._pfx: self._ns_uri} - - @property - def nspfx(self): - """ - Return the string namespace prefix for the tag, e.g. 'f' is returned - for tag 'f:foobar'. - """ - return self._pfx - - @property - def nsuri(self): - """ - Return the namespace URI for the tag, e.g. 'http://foo/bar' would be - returned for tag 'f:foobar' if the 'f' prefix maps to - 'http://foo/bar' in nsmap. - """ - return self._ns_uri - - -def nsdecls(*prefixes): - """ - Return a string containing a namespace declaration for each of the - namespace prefix strings, e.g. 'p', 'ct', passed as *prefixes*. - """ - return ' '.join(['xmlns:%s="%s"' % (pfx, nsmap[pfx]) for pfx in prefixes]) - - -def nspfxmap(*nspfxs): - """ - Return a dict containing the subset namespace prefix mappings specified by - *nspfxs*. Any number of namespace prefixes can be supplied, e.g. - namespaces('a', 'r', 'p'). - """ - return dict((pfx, nsmap[pfx]) for pfx in nspfxs) - - -def qn(tag): - """ - Stands for "qualified name", a utility function to turn a namespace - prefixed tag name into a Clark-notation qualified tag name for lxml. For - example, ``qn('p:cSld')`` returns ``'{http://schemas.../main}cSld'``. - """ - prefix, tagroot = tag.split(':') - uri = nsmap[prefix] - return '{%s}%s' % (uri, tagroot) diff --git a/build/lib/docx/oxml/numbering.py b/build/lib/docx/oxml/numbering.py deleted file mode 100644 index aeedfa9a0..000000000 --- a/build/lib/docx/oxml/numbering.py +++ /dev/null @@ -1,131 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes related to the numbering part -""" - -from . import OxmlElement -from .shared import CT_DecimalNumber -from .simpletypes import ST_DecimalNumber -from .xmlchemy import ( - BaseOxmlElement, OneAndOnlyOne, RequiredAttribute, ZeroOrMore, ZeroOrOne -) - - -class CT_Num(BaseOxmlElement): - """ - ```` element, which represents a concrete list definition - instance, having a required child that references an - abstract numbering definition that defines most of the formatting details. - """ - abstractNumId = OneAndOnlyOne('w:abstractNumId') - lvlOverride = ZeroOrMore('w:lvlOverride') - numId = RequiredAttribute('w:numId', ST_DecimalNumber) - - def add_lvlOverride(self, ilvl): - """ - Return a newly added CT_NumLvl () element having its - ``ilvl`` attribute set to *ilvl*. - """ - return self._add_lvlOverride(ilvl=ilvl) - - @classmethod - def new(cls, num_id, abstractNum_id): - """ - Return a new ```` element having numId of *num_id* and having - a ```` child with val attribute set to - *abstractNum_id*. - """ - num = OxmlElement('w:num') - num.numId = num_id - abstractNumId = CT_DecimalNumber.new( - 'w:abstractNumId', abstractNum_id - ) - num.append(abstractNumId) - return num - - -class CT_NumLvl(BaseOxmlElement): - """ - ```` element, which identifies a level in a list - definition to override with settings it contains. - """ - startOverride = ZeroOrOne('w:startOverride', successors=('w:lvl',)) - ilvl = RequiredAttribute('w:ilvl', ST_DecimalNumber) - - def add_startOverride(self, val): - """ - Return a newly added CT_DecimalNumber element having tagname - ``w:startOverride`` and ``val`` attribute set to *val*. - """ - return self._add_startOverride(val=val) - - -class CT_NumPr(BaseOxmlElement): - """ - A ```` element, a container for numbering properties applied to - a paragraph. - """ - ilvl = ZeroOrOne('w:ilvl', successors=( - 'w:numId', 'w:numberingChange', 'w:ins' - )) - numId = ZeroOrOne('w:numId', successors=('w:numberingChange', 'w:ins')) - - # @ilvl.setter - # def _set_ilvl(self, val): - # """ - # Get or add a child and set its ``w:val`` attribute to *val*. - # """ - # ilvl = self.get_or_add_ilvl() - # ilvl.val = val - - # @numId.setter - # def numId(self, val): - # """ - # Get or add a child and set its ``w:val`` attribute to - # *val*. - # """ - # numId = self.get_or_add_numId() - # numId.val = val - - -class CT_Numbering(BaseOxmlElement): - """ - ```` element, the root element of a numbering part, i.e. - numbering.xml - """ - num = ZeroOrMore('w:num', successors=('w:numIdMacAtCleanup',)) - - def add_num(self, abstractNum_id): - """ - Return a newly added CT_Num () element referencing the - abstract numbering definition identified by *abstractNum_id*. - """ - next_num_id = self._next_numId - num = CT_Num.new(next_num_id, abstractNum_id) - return self._insert_num(num) - - def num_having_numId(self, numId): - """ - Return the ```` child element having ``numId`` attribute - matching *numId*. - """ - xpath = './w:num[@w:numId="%d"]' % numId - try: - return self.xpath(xpath)[0] - except IndexError: - raise KeyError('no element with numId %d' % numId) - - @property - def _next_numId(self): - """ - The first ``numId`` unused by a ```` element, starting at - 1 and filling any gaps in numbering between existing ```` - elements. - """ - numId_strs = self.xpath('./w:num/@w:numId') - num_ids = [int(numId_str) for numId_str in numId_strs] - for num in range(1, len(num_ids)+2): - if num not in num_ids: - break - return num diff --git a/build/lib/docx/oxml/section.py b/build/lib/docx/oxml/section.py deleted file mode 100644 index cf76b67ed..000000000 --- a/build/lib/docx/oxml/section.py +++ /dev/null @@ -1,264 +0,0 @@ -# encoding: utf-8 - -""" -Section-related custom element classes. -""" - -from __future__ import absolute_import, print_function - -from copy import deepcopy - -from ..enum.section import WD_ORIENTATION, WD_SECTION_START -from .simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure -from .xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrOne - - -class CT_PageMar(BaseOxmlElement): - """ - ```` element, defining page margins. - """ - top = OptionalAttribute('w:top', ST_SignedTwipsMeasure) - right = OptionalAttribute('w:right', ST_TwipsMeasure) - bottom = OptionalAttribute('w:bottom', ST_SignedTwipsMeasure) - left = OptionalAttribute('w:left', ST_TwipsMeasure) - header = OptionalAttribute('w:header', ST_TwipsMeasure) - footer = OptionalAttribute('w:footer', ST_TwipsMeasure) - gutter = OptionalAttribute('w:gutter', ST_TwipsMeasure) - - -class CT_PageSz(BaseOxmlElement): - """ - ```` element, defining page dimensions and orientation. - """ - w = OptionalAttribute('w:w', ST_TwipsMeasure) - h = OptionalAttribute('w:h', ST_TwipsMeasure) - orient = OptionalAttribute( - 'w:orient', WD_ORIENTATION, default=WD_ORIENTATION.PORTRAIT - ) - - -class CT_SectPr(BaseOxmlElement): - """ - ```` element, the container element for section properties. - """ - __child_sequence__ = ( - 'w:footnotePr', 'w:endnotePr', 'w:type', 'w:pgSz', 'w:pgMar', - 'w:paperSrc', 'w:pgBorders', 'w:lnNumType', 'w:pgNumType', 'w:cols', - 'w:formProt', 'w:vAlign', 'w:noEndnote', 'w:titlePg', - 'w:textDirection', 'w:bidi', 'w:rtlGutter', 'w:docGrid', - 'w:printerSettings', 'w:sectPrChange', - ) - type = ZeroOrOne('w:type', successors=( - __child_sequence__[__child_sequence__.index('w:type')+1:] - )) - pgSz = ZeroOrOne('w:pgSz', successors=( - __child_sequence__[__child_sequence__.index('w:pgSz')+1:] - )) - pgMar = ZeroOrOne('w:pgMar', successors=( - __child_sequence__[__child_sequence__.index('w:pgMar')+1:] - )) - - @property - def bottom_margin(self): - """ - The value of the ``w:bottom`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.bottom - - @bottom_margin.setter - def bottom_margin(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.bottom = value - - def clone(self): - """ - Return an exact duplicate of this ```` element tree - suitable for use in adding a section break. All rsid* attributes are - removed from the root ```` element. - """ - clone_sectPr = deepcopy(self) - clone_sectPr.attrib.clear() - return clone_sectPr - - @property - def footer(self): - """ - The value of the ``w:footer`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.footer - - @footer.setter - def footer(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.footer = value - - @property - def gutter(self): - """ - The value of the ``w:gutter`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.gutter - - @gutter.setter - def gutter(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.gutter = value - - @property - def header(self): - """ - The value of the ``w:header`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.header - - @header.setter - def header(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.header = value - - @property - def left_margin(self): - """ - The value of the ``w:left`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.left - - @left_margin.setter - def left_margin(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.left = value - - @property - def right_margin(self): - """ - The value of the ``w:right`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.right - - @right_margin.setter - def right_margin(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.right = value - - @property - def orientation(self): - """ - The member of the ``WD_ORIENTATION`` enumeration corresponding to the - value of the ``orient`` attribute of the ```` child element, - or ``WD_ORIENTATION.PORTRAIT`` if not present. - """ - pgSz = self.pgSz - if pgSz is None: - return WD_ORIENTATION.PORTRAIT - return pgSz.orient - - @orientation.setter - def orientation(self, value): - pgSz = self.get_or_add_pgSz() - pgSz.orient = value - - @property - def page_height(self): - """ - Value in EMU of the ``h`` attribute of the ```` child - element, or |None| if not present. - """ - pgSz = self.pgSz - if pgSz is None: - return None - return pgSz.h - - @page_height.setter - def page_height(self, value): - pgSz = self.get_or_add_pgSz() - pgSz.h = value - - @property - def page_width(self): - """ - Value in EMU of the ``w`` attribute of the ```` child - element, or |None| if not present. - """ - pgSz = self.pgSz - if pgSz is None: - return None - return pgSz.w - - @page_width.setter - def page_width(self, value): - pgSz = self.get_or_add_pgSz() - pgSz.w = value - - @property - def start_type(self): - """ - The member of the ``WD_SECTION_START`` enumeration corresponding to - the value of the ``val`` attribute of the ```` child element, - or ``WD_SECTION_START.NEW_PAGE`` if not present. - """ - type = self.type - if type is None or type.val is None: - return WD_SECTION_START.NEW_PAGE - return type.val - - @start_type.setter - def start_type(self, value): - if value is None or value is WD_SECTION_START.NEW_PAGE: - self._remove_type() - return - type = self.get_or_add_type() - type.val = value - - @property - def top_margin(self): - """ - The value of the ``w:top`` attribute in the ```` child - element, as a |Length| object, or |None| if either the element or the - attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.top - - @top_margin.setter - def top_margin(self, value): - pgMar = self.get_or_add_pgMar() - pgMar.top = value - - -class CT_SectType(BaseOxmlElement): - """ - ```` element, defining the section start type. - """ - val = OptionalAttribute('w:val', WD_SECTION_START) diff --git a/build/lib/docx/oxml/shape.py b/build/lib/docx/oxml/shape.py deleted file mode 100644 index 77ca7db8a..000000000 --- a/build/lib/docx/oxml/shape.py +++ /dev/null @@ -1,284 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes for shape-related elements like ```` -""" - -from . import parse_xml -from .ns import nsdecls -from .simpletypes import ( - ST_Coordinate, ST_DrawingElementId, ST_PositiveCoordinate, - ST_RelationshipId, XsdString, XsdToken -) -from .xmlchemy import ( - BaseOxmlElement, OneAndOnlyOne, OptionalAttribute, RequiredAttribute, - ZeroOrOne -) - - -class CT_Blip(BaseOxmlElement): - """ - ```` element, specifies image source and adjustments such as - alpha and tint. - """ - embed = OptionalAttribute('r:embed', ST_RelationshipId) - link = OptionalAttribute('r:link', ST_RelationshipId) - - -class CT_BlipFillProperties(BaseOxmlElement): - """ - ```` element, specifies picture properties - """ - blip = ZeroOrOne('a:blip', successors=( - 'a:srcRect', 'a:tile', 'a:stretch' - )) - - -class CT_GraphicalObject(BaseOxmlElement): - """ - ```` element, container for a DrawingML object - """ - graphicData = OneAndOnlyOne('a:graphicData') - - -class CT_GraphicalObjectData(BaseOxmlElement): - """ - ```` element, container for the XML of a DrawingML object - """ - pic = ZeroOrOne('pic:pic') - uri = RequiredAttribute('uri', XsdToken) - - -class CT_Inline(BaseOxmlElement): - """ - ```` element, container for an inline shape. - """ - extent = OneAndOnlyOne('wp:extent') - docPr = OneAndOnlyOne('wp:docPr') - graphic = OneAndOnlyOne('a:graphic') - - @classmethod - def new(cls, cx, cy, shape_id, pic): - """ - Return a new ```` element populated with the values passed - as parameters. - """ - inline = parse_xml(cls._inline_xml()) - inline.extent.cx = cx - inline.extent.cy = cy - inline.docPr.id = shape_id - inline.docPr.name = 'Picture %d' % shape_id - inline.graphic.graphicData.uri = ( - 'http://schemas.openxmlformats.org/drawingml/2006/picture' - ) - inline.graphic.graphicData._insert_pic(pic) - return inline - - @classmethod - def new_pic_inline(cls, shape_id, rId, filename, cx, cy): - """ - Return a new `wp:inline` element containing the `pic:pic` element - specified by the argument values. - """ - pic_id = 0 # Word doesn't seem to use this, but does not omit it - pic = CT_Picture.new(pic_id, filename, rId, cx, cy) - inline = cls.new(cx, cy, shape_id, pic) - inline.graphic.graphicData._insert_pic(pic) - return inline - - @classmethod - def _inline_xml(cls): - return ( - '\n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - '' % nsdecls('wp', 'a', 'pic', 'r') - ) - - -class CT_NonVisualDrawingProps(BaseOxmlElement): - """ - Used for ```` element, and perhaps others. Specifies the id and - name of a DrawingML drawing. - """ - id = RequiredAttribute('id', ST_DrawingElementId) - name = RequiredAttribute('name', XsdString) - - -class CT_NonVisualPictureProperties(BaseOxmlElement): - """ - ```` element, specifies picture locking and resize - behaviors. - """ - - -class CT_Picture(BaseOxmlElement): - """ - ```` element, a DrawingML picture - """ - nvPicPr = OneAndOnlyOne('pic:nvPicPr') - blipFill = OneAndOnlyOne('pic:blipFill') - spPr = OneAndOnlyOne('pic:spPr') - - @classmethod - def new(cls, pic_id, filename, rId, cx, cy): - """ - Return a new ```` element populated with the minimal - contents required to define a viable picture element, based on the - values passed as parameters. - """ - pic = parse_xml(cls._pic_xml()) - pic.nvPicPr.cNvPr.id = pic_id - pic.nvPicPr.cNvPr.name = filename - pic.blipFill.blip.embed = rId - pic.spPr.cx = cx - pic.spPr.cy = cy - return pic - - @classmethod - def _pic_xml(cls): - return ( - '\n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - '' % nsdecls('pic', 'a', 'r') - ) - - -class CT_PictureNonVisual(BaseOxmlElement): - """ - ```` element, non-visual picture properties - """ - cNvPr = OneAndOnlyOne('pic:cNvPr') - - -class CT_Point2D(BaseOxmlElement): - """ - Used for ```` element, and perhaps others. Specifies an x, y - coordinate (point). - """ - x = RequiredAttribute('x', ST_Coordinate) - y = RequiredAttribute('y', ST_Coordinate) - - -class CT_PositiveSize2D(BaseOxmlElement): - """ - Used for ```` element, and perhaps others later. Specifies the - size of a DrawingML drawing. - """ - cx = RequiredAttribute('cx', ST_PositiveCoordinate) - cy = RequiredAttribute('cy', ST_PositiveCoordinate) - - -class CT_PresetGeometry2D(BaseOxmlElement): - """ - ```` element, specifies an preset autoshape geometry, such - as ``rect``. - """ - - -class CT_RelativeRect(BaseOxmlElement): - """ - ```` element, specifying picture should fill containing - rectangle shape. - """ - - -class CT_ShapeProperties(BaseOxmlElement): - """ - ```` element, specifies size and shape of picture container. - """ - xfrm = ZeroOrOne('a:xfrm', successors=( - 'a:custGeom', 'a:prstGeom', 'a:ln', 'a:effectLst', 'a:effectDag', - 'a:scene3d', 'a:sp3d', 'a:extLst' - )) - - @property - def cx(self): - """ - Shape width as an instance of Emu, or None if not present. - """ - xfrm = self.xfrm - if xfrm is None: - return None - return xfrm.cx - - @cx.setter - def cx(self, value): - xfrm = self.get_or_add_xfrm() - xfrm.cx = value - - @property - def cy(self): - """ - Shape height as an instance of Emu, or None if not present. - """ - xfrm = self.xfrm - if xfrm is None: - return None - return xfrm.cy - - @cy.setter - def cy(self, value): - xfrm = self.get_or_add_xfrm() - xfrm.cy = value - - -class CT_StretchInfoProperties(BaseOxmlElement): - """ - ```` element, specifies how picture should fill its containing - shape. - """ - - -class CT_Transform2D(BaseOxmlElement): - """ - ```` element, specifies size and shape of picture container. - """ - off = ZeroOrOne('a:off', successors=('a:ext',)) - ext = ZeroOrOne('a:ext', successors=()) - - @property - def cx(self): - ext = self.ext - if ext is None: - return None - return ext.cx - - @cx.setter - def cx(self, value): - ext = self.get_or_add_ext() - ext.cx = value - - @property - def cy(self): - ext = self.ext - if ext is None: - return None - return ext.cy - - @cy.setter - def cy(self, value): - ext = self.get_or_add_ext() - ext.cy = value diff --git a/build/lib/docx/oxml/shared.py b/build/lib/docx/oxml/shared.py deleted file mode 100644 index 1e21ba366..000000000 --- a/build/lib/docx/oxml/shared.py +++ /dev/null @@ -1,55 +0,0 @@ -# encoding: utf-8 - -""" -Objects shared by modules in the docx.oxml subpackage. -""" - -from __future__ import absolute_import - -from . import OxmlElement -from .ns import qn -from .simpletypes import ST_DecimalNumber, ST_OnOff, ST_String -from .xmlchemy import BaseOxmlElement, OptionalAttribute, RequiredAttribute - - -class CT_DecimalNumber(BaseOxmlElement): - """ - Used for ````, ````, ```` and several - others, containing a text representation of a decimal number (e.g. 42) in - its ``val`` attribute. - """ - val = RequiredAttribute('w:val', ST_DecimalNumber) - - @classmethod - def new(cls, nsptagname, val): - """ - Return a new ``CT_DecimalNumber`` element having tagname *nsptagname* - and ``val`` attribute set to *val*. - """ - return OxmlElement(nsptagname, attrs={qn('w:val'): str(val)}) - - -class CT_OnOff(BaseOxmlElement): - """ - Used for ````, ```` elements and others, containing a bool-ish - string in its ``val`` attribute, xsd:boolean plus 'on' and 'off'. - """ - val = OptionalAttribute('w:val', ST_OnOff, default=True) - - -class CT_String(BaseOxmlElement): - """ - Used for ```` and ```` elements and others, - containing a style name in its ``val`` attribute. - """ - val = RequiredAttribute('w:val', ST_String) - - @classmethod - def new(cls, nsptagname, val): - """ - Return a new ``CT_String`` element with tagname *nsptagname* and - ``val`` attribute set to *val*. - """ - elm = OxmlElement(nsptagname) - elm.val = val - return elm diff --git a/build/lib/docx/oxml/simpletypes.py b/build/lib/docx/oxml/simpletypes.py deleted file mode 100644 index 400a23700..000000000 --- a/build/lib/docx/oxml/simpletypes.py +++ /dev/null @@ -1,409 +0,0 @@ -# encoding: utf-8 - -""" -Simple type classes, providing validation and format translation for values -stored in XML element attributes. Naming generally corresponds to the simple -type in the associated XML schema. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..exceptions import InvalidXmlError -from ..shared import Emu, Pt, RGBColor, Twips - - -class BaseSimpleType(object): - - @classmethod - def from_xml(cls, str_value): - return cls.convert_from_xml(str_value) - - @classmethod - def to_xml(cls, value): - cls.validate(value) - str_value = cls.convert_to_xml(value) - return str_value - - @classmethod - def validate_int(cls, value): - if not isinstance(value, int): - raise TypeError( - "value must be , got %s" % type(value) - ) - - @classmethod - def validate_int_in_range(cls, value, min_inclusive, max_inclusive): - cls.validate_int(value) - if value < min_inclusive or value > max_inclusive: - raise ValueError( - "value must be in range %d to %d inclusive, got %d" % - (min_inclusive, max_inclusive, value) - ) - - @classmethod - def validate_string(cls, value): - if isinstance(value, str): - return value - try: - if isinstance(value, basestring): - return value - except NameError: # means we're on Python 3 - pass - raise TypeError( - "value must be a string, got %s" % type(value) - ) - - -class BaseIntType(BaseSimpleType): - - @classmethod - def convert_from_xml(cls, str_value): - return int(str_value) - - @classmethod - def convert_to_xml(cls, value): - return str(value) - - @classmethod - def validate(cls, value): - cls.validate_int(value) - - -class BaseStringType(BaseSimpleType): - - @classmethod - def convert_from_xml(cls, str_value): - return str_value - - @classmethod - def convert_to_xml(cls, value): - return value - - @classmethod - def validate(cls, value): - cls.validate_string(value) - - -class BaseStringEnumerationType(BaseStringType): - - @classmethod - def validate(cls, value): - cls.validate_string(value) - if value not in cls._members: - raise ValueError( - "must be one of %s, got '%s'" % (cls._members, value) - ) - - -class XsdAnyUri(BaseStringType): - """ - There's a regular expression this is supposed to meet but so far thinking - spending cycles on validating wouldn't be worth it for the number of - programming errors it would catch. - """ - - -class XsdBoolean(BaseSimpleType): - - @classmethod - def convert_from_xml(cls, str_value): - if str_value not in ('1', '0', 'true', 'false'): - raise InvalidXmlError( - "value must be one of '1', '0', 'true' or 'false', got '%s'" - % str_value - ) - return str_value in ('1', 'true') - - @classmethod - def convert_to_xml(cls, value): - return {True: '1', False: '0'}[value] - - @classmethod - def validate(cls, value): - if value not in (True, False): - raise TypeError( - "only True or False (and possibly None) may be assigned, got" - " '%s'" % value - ) - - -class XsdId(BaseStringType): - """ - String that must begin with a letter or underscore and cannot contain any - colons. Not fully validated because not used in external API. - """ - pass - - -class XsdInt(BaseIntType): - - @classmethod - def validate(cls, value): - cls.validate_int_in_range(value, -2147483648, 2147483647) - - -class XsdLong(BaseIntType): - - @classmethod - def validate(cls, value): - cls.validate_int_in_range( - value, -9223372036854775808, 9223372036854775807 - ) - - -class XsdString(BaseStringType): - pass - - -class XsdStringEnumeration(BaseStringEnumerationType): - """ - Set of enumerated xsd:string values. - """ - - -class XsdToken(BaseStringType): - """ - xsd:string with whitespace collapsing, e.g. multiple spaces reduced to - one, leading and trailing space stripped. - """ - pass - - -class XsdUnsignedInt(BaseIntType): - - @classmethod - def validate(cls, value): - cls.validate_int_in_range(value, 0, 4294967295) - - -class XsdUnsignedLong(BaseIntType): - - @classmethod - def validate(cls, value): - cls.validate_int_in_range(value, 0, 18446744073709551615) - - -class ST_BrClear(XsdString): - - @classmethod - def validate(cls, value): - cls.validate_string(value) - valid_values = ('none', 'left', 'right', 'all') - if value not in valid_values: - raise ValueError( - "must be one of %s, got '%s'" % (valid_values, value) - ) - - -class ST_BrType(XsdString): - - @classmethod - def validate(cls, value): - cls.validate_string(value) - valid_values = ('page', 'column', 'textWrapping') - if value not in valid_values: - raise ValueError( - "must be one of %s, got '%s'" % (valid_values, value) - ) - - -class ST_Coordinate(BaseIntType): - - @classmethod - def convert_from_xml(cls, str_value): - if 'i' in str_value or 'm' in str_value or 'p' in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Emu(int(str_value)) - - @classmethod - def validate(cls, value): - ST_CoordinateUnqualified.validate(value) - - -class ST_CoordinateUnqualified(XsdLong): - - @classmethod - def validate(cls, value): - cls.validate_int_in_range(value, -27273042329600, 27273042316900) - - -class ST_DecimalNumber(XsdInt): - pass - - -class ST_DrawingElementId(XsdUnsignedInt): - pass - - -class ST_HexColor(BaseStringType): - - @classmethod - def convert_from_xml(cls, str_value): - if str_value == 'auto': - return ST_HexColorAuto.AUTO - return RGBColor.from_string(str_value) - - @classmethod - def convert_to_xml(cls, value): - """ - Keep alpha hex numerals all uppercase just for consistency. - """ - # expecting 3-tuple of ints in range 0-255 - return '%02X%02X%02X' % value - - @classmethod - def validate(cls, value): - # must be an RGBColor object --- - if not isinstance(value, RGBColor): - raise ValueError( - "rgb color value must be RGBColor object, got %s %s" - % (type(value), value) - ) - - -class ST_HexColorAuto(XsdStringEnumeration): - """ - Value for `w:color/[@val="auto"] attribute setting - """ - AUTO = 'auto' - - _members = (AUTO,) - - -class ST_HpsMeasure(XsdUnsignedLong): - """ - Half-point measure, e.g. 24.0 represents 12.0 points. - """ - @classmethod - def convert_from_xml(cls, str_value): - if 'm' in str_value or 'n' in str_value or 'p' in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Pt(int(str_value)/2.0) - - @classmethod - def convert_to_xml(cls, value): - emu = Emu(value) - half_points = int(emu.pt * 2) - return str(half_points) - - -class ST_Merge(XsdStringEnumeration): - """ - Valid values for attribute - """ - CONTINUE = 'continue' - RESTART = 'restart' - - _members = (CONTINUE, RESTART) - - -class ST_OnOff(XsdBoolean): - - @classmethod - def convert_from_xml(cls, str_value): - if str_value not in ('1', '0', 'true', 'false', 'on', 'off'): - raise InvalidXmlError( - "value must be one of '1', '0', 'true', 'false', 'on', or 'o" - "ff', got '%s'" % str_value - ) - return str_value in ('1', 'true', 'on') - - -class ST_PositiveCoordinate(XsdLong): - - @classmethod - def convert_from_xml(cls, str_value): - return Emu(int(str_value)) - - @classmethod - def validate(cls, value): - cls.validate_int_in_range(value, 0, 27273042316900) - - -class ST_RelationshipId(XsdString): - pass - - -class ST_SignedTwipsMeasure(XsdInt): - - @classmethod - def convert_from_xml(cls, str_value): - if 'i' in str_value or 'm' in str_value or 'p' in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Twips(int(str_value)) - - @classmethod - def convert_to_xml(cls, value): - emu = Emu(value) - twips = emu.twips - return str(twips) - - -class ST_String(XsdString): - pass - - -class ST_TblLayoutType(XsdString): - - @classmethod - def validate(cls, value): - cls.validate_string(value) - valid_values = ('fixed', 'autofit') - if value not in valid_values: - raise ValueError( - "must be one of %s, got '%s'" % (valid_values, value) - ) - - -class ST_TblWidth(XsdString): - - @classmethod - def validate(cls, value): - cls.validate_string(value) - valid_values = ('auto', 'dxa', 'nil', 'pct') - if value not in valid_values: - raise ValueError( - "must be one of %s, got '%s'" % (valid_values, value) - ) - - -class ST_TwipsMeasure(XsdUnsignedLong): - - @classmethod - def convert_from_xml(cls, str_value): - if 'i' in str_value or 'm' in str_value or 'p' in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Twips(int(str_value)) - - @classmethod - def convert_to_xml(cls, value): - emu = Emu(value) - twips = emu.twips - return str(twips) - - -class ST_UniversalMeasure(BaseSimpleType): - - @classmethod - def convert_from_xml(cls, str_value): - float_part, units_part = str_value[:-2], str_value[-2:] - quantity = float(float_part) - multiplier = { - 'mm': 36000, 'cm': 360000, 'in': 914400, 'pt': 12700, - 'pc': 152400, 'pi': 152400 - }[units_part] - emu_value = Emu(int(round(quantity * multiplier))) - return emu_value - - -class ST_VerticalAlignRun(XsdStringEnumeration): - """ - Valid values for `w:vertAlign/@val`. - """ - BASELINE = 'baseline' - SUPERSCRIPT = 'superscript' - SUBSCRIPT = 'subscript' - - _members = (BASELINE, SUPERSCRIPT, SUBSCRIPT) diff --git a/build/lib/docx/oxml/styles.py b/build/lib/docx/oxml/styles.py deleted file mode 100644 index 6f27e45eb..000000000 --- a/build/lib/docx/oxml/styles.py +++ /dev/null @@ -1,351 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes related to the styles part -""" - -from ..enum.style import WD_STYLE_TYPE -from .simpletypes import ST_DecimalNumber, ST_OnOff, ST_String -from .xmlchemy import ( - BaseOxmlElement, OptionalAttribute, RequiredAttribute, ZeroOrMore, - ZeroOrOne -) - - -def styleId_from_name(name): - """ - Return the style id corresponding to *name*, taking into account - special-case names such as 'Heading 1'. - """ - return { - 'caption': 'Caption', - 'heading 1': 'Heading1', - 'heading 2': 'Heading2', - 'heading 3': 'Heading3', - 'heading 4': 'Heading4', - 'heading 5': 'Heading5', - 'heading 6': 'Heading6', - 'heading 7': 'Heading7', - 'heading 8': 'Heading8', - 'heading 9': 'Heading9', - }.get(name, name.replace(' ', '')) - - -class CT_LatentStyles(BaseOxmlElement): - """ - `w:latentStyles` element, defining behavior defaults for latent styles - and containing `w:lsdException` child elements that each override those - defaults for a named latent style. - """ - lsdException = ZeroOrMore('w:lsdException', successors=()) - - count = OptionalAttribute('w:count', ST_DecimalNumber) - defLockedState = OptionalAttribute('w:defLockedState', ST_OnOff) - defQFormat = OptionalAttribute('w:defQFormat', ST_OnOff) - defSemiHidden = OptionalAttribute('w:defSemiHidden', ST_OnOff) - defUIPriority = OptionalAttribute('w:defUIPriority', ST_DecimalNumber) - defUnhideWhenUsed = OptionalAttribute('w:defUnhideWhenUsed', ST_OnOff) - - def bool_prop(self, attr_name): - """ - Return the boolean value of the attribute having *attr_name*, or - |False| if not present. - """ - value = getattr(self, attr_name) - if value is None: - return False - return value - - def get_by_name(self, name): - """ - Return the `w:lsdException` child having *name*, or |None| if not - found. - """ - found = self.xpath('w:lsdException[@w:name="%s"]' % name) - if not found: - return None - return found[0] - - def set_bool_prop(self, attr_name, value): - """ - Set the on/off attribute having *attr_name* to *value*. - """ - setattr(self, attr_name, bool(value)) - - -class CT_LsdException(BaseOxmlElement): - """ - ```` element, defining override visibility behaviors for - a named latent style. - """ - locked = OptionalAttribute('w:locked', ST_OnOff) - name = RequiredAttribute('w:name', ST_String) - qFormat = OptionalAttribute('w:qFormat', ST_OnOff) - semiHidden = OptionalAttribute('w:semiHidden', ST_OnOff) - uiPriority = OptionalAttribute('w:uiPriority', ST_DecimalNumber) - unhideWhenUsed = OptionalAttribute('w:unhideWhenUsed', ST_OnOff) - - def delete(self): - """ - Remove this `w:lsdException` element from the XML document. - """ - self.getparent().remove(self) - - def on_off_prop(self, attr_name): - """ - Return the boolean value of the attribute having *attr_name*, or - |None| if not present. - """ - return getattr(self, attr_name) - - def set_on_off_prop(self, attr_name, value): - """ - Set the on/off attribute having *attr_name* to *value*. - """ - setattr(self, attr_name, value) - - -class CT_Style(BaseOxmlElement): - """ - A ```` element, representing a style definition - """ - _tag_seq = ( - 'w:name', 'w:aliases', 'w:basedOn', 'w:next', 'w:link', - 'w:autoRedefine', 'w:hidden', 'w:uiPriority', 'w:semiHidden', - 'w:unhideWhenUsed', 'w:qFormat', 'w:locked', 'w:personal', - 'w:personalCompose', 'w:personalReply', 'w:rsid', 'w:pPr', 'w:rPr', - 'w:tblPr', 'w:trPr', 'w:tcPr', 'w:tblStylePr' - ) - name = ZeroOrOne('w:name', successors=_tag_seq[1:]) - basedOn = ZeroOrOne('w:basedOn', successors=_tag_seq[3:]) - next = ZeroOrOne('w:next', successors=_tag_seq[4:]) - uiPriority = ZeroOrOne('w:uiPriority', successors=_tag_seq[8:]) - semiHidden = ZeroOrOne('w:semiHidden', successors=_tag_seq[9:]) - unhideWhenUsed = ZeroOrOne('w:unhideWhenUsed', successors=_tag_seq[10:]) - qFormat = ZeroOrOne('w:qFormat', successors=_tag_seq[11:]) - locked = ZeroOrOne('w:locked', successors=_tag_seq[12:]) - pPr = ZeroOrOne('w:pPr', successors=_tag_seq[17:]) - rPr = ZeroOrOne('w:rPr', successors=_tag_seq[18:]) - del _tag_seq - - type = OptionalAttribute('w:type', WD_STYLE_TYPE) - styleId = OptionalAttribute('w:styleId', ST_String) - default = OptionalAttribute('w:default', ST_OnOff) - customStyle = OptionalAttribute('w:customStyle', ST_OnOff) - - @property - def basedOn_val(self): - """ - Value of `w:basedOn/@w:val` or |None| if not present. - """ - basedOn = self.basedOn - if basedOn is None: - return None - return basedOn.val - - @basedOn_val.setter - def basedOn_val(self, value): - if value is None: - self._remove_basedOn() - else: - self.get_or_add_basedOn().val = value - - @property - def base_style(self): - """ - Sibling CT_Style element this style is based on or |None| if no base - style or base style not found. - """ - basedOn = self.basedOn - if basedOn is None: - return None - styles = self.getparent() - base_style = styles.get_by_id(basedOn.val) - if base_style is None: - return None - return base_style - - def delete(self): - """ - Remove this `w:style` element from its parent `w:styles` element. - """ - self.getparent().remove(self) - - @property - def locked_val(self): - """ - Value of `w:locked/@w:val` or |False| if not present. - """ - locked = self.locked - if locked is None: - return False - return locked.val - - @locked_val.setter - def locked_val(self, value): - self._remove_locked() - if bool(value) is True: - locked = self._add_locked() - locked.val = value - - @property - def name_val(self): - """ - Value of ```` child or |None| if not present. - """ - name = self.name - if name is None: - return None - return name.val - - @name_val.setter - def name_val(self, value): - self._remove_name() - if value is not None: - name = self._add_name() - name.val = value - - @property - def next_style(self): - """ - Sibling CT_Style element identified by the value of `w:name/@w:val` - or |None| if no value is present or no style with that style id - is found. - """ - next = self.next - if next is None: - return None - styles = self.getparent() - return styles.get_by_id(next.val) # None if not found - - @property - def qFormat_val(self): - """ - Value of `w:qFormat/@w:val` or |False| if not present. - """ - qFormat = self.qFormat - if qFormat is None: - return False - return qFormat.val - - @qFormat_val.setter - def qFormat_val(self, value): - self._remove_qFormat() - if bool(value): - self._add_qFormat() - - @property - def semiHidden_val(self): - """ - Value of ```` child or |False| if not present. - """ - semiHidden = self.semiHidden - if semiHidden is None: - return False - return semiHidden.val - - @semiHidden_val.setter - def semiHidden_val(self, value): - self._remove_semiHidden() - if bool(value) is True: - semiHidden = self._add_semiHidden() - semiHidden.val = value - - @property - def uiPriority_val(self): - """ - Value of ```` child or |None| if not present. - """ - uiPriority = self.uiPriority - if uiPriority is None: - return None - return uiPriority.val - - @uiPriority_val.setter - def uiPriority_val(self, value): - self._remove_uiPriority() - if value is not None: - uiPriority = self._add_uiPriority() - uiPriority.val = value - - @property - def unhideWhenUsed_val(self): - """ - Value of `w:unhideWhenUsed/@w:val` or |False| if not present. - """ - unhideWhenUsed = self.unhideWhenUsed - if unhideWhenUsed is None: - return False - return unhideWhenUsed.val - - @unhideWhenUsed_val.setter - def unhideWhenUsed_val(self, value): - self._remove_unhideWhenUsed() - if bool(value) is True: - unhideWhenUsed = self._add_unhideWhenUsed() - unhideWhenUsed.val = value - - -class CT_Styles(BaseOxmlElement): - """ - ```` element, the root element of a styles part, i.e. - styles.xml - """ - _tag_seq = ('w:docDefaults', 'w:latentStyles', 'w:style') - latentStyles = ZeroOrOne('w:latentStyles', successors=_tag_seq[2:]) - style = ZeroOrMore('w:style', successors=()) - del _tag_seq - - def add_style_of_type(self, name, style_type, builtin): - """ - Return a newly added `w:style` element having *name* and - *style_type*. `w:style/@customStyle` is set based on the value of - *builtin*. - """ - style = self.add_style() - style.type = style_type - style.customStyle = None if builtin else True - style.styleId = styleId_from_name(name) - style.name_val = name - return style - - def default_for(self, style_type): - """ - Return `w:style[@w:type="*{style_type}*][-1]` or |None| if not found. - """ - default_styles_for_type = [ - s for s in self._iter_styles() - if s.type == style_type and s.default - ] - if not default_styles_for_type: - return None - # spec calls for last default in document order - return default_styles_for_type[-1] - - def get_by_id(self, styleId): - """ - Return the ```` child element having ``styleId`` attribute - matching *styleId*, or |None| if not found. - """ - xpath = 'w:style[@w:styleId="%s"]' % styleId - try: - return self.xpath(xpath)[0] - except IndexError: - return None - - def get_by_name(self, name): - """ - Return the ```` child element having ```` child - element with value *name*, or |None| if not found. - """ - xpath = 'w:style[w:name/@w:val="%s"]' % name - try: - return self.xpath(xpath)[0] - except IndexError: - return None - - def _iter_styles(self): - """ - Generate each of the `w:style` child elements in document order. - """ - return (style for style in self.xpath('w:style')) diff --git a/build/lib/docx/oxml/table.py b/build/lib/docx/oxml/table.py deleted file mode 100644 index 403aa88a5..000000000 --- a/build/lib/docx/oxml/table.py +++ /dev/null @@ -1,954 +0,0 @@ -# encoding: utf-8 - -"""Custom element classes for tables""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from . import parse_xml -from . import OxmlElement -from ..enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE -from ..exceptions import InvalidSpanError -from .ns import nsdecls, qn -from ..shared import Emu, Twips -from .simpletypes import ( - ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt, ST_String -) -from .xmlchemy import ( - BaseOxmlElement, OneAndOnlyOne, OneOrMore, OptionalAttribute, - RequiredAttribute, ZeroOrOne, ZeroOrMore -) - - -class CT_Height(BaseOxmlElement): - """ - Used for ```` to specify a row height and row height rule. - """ - val = OptionalAttribute('w:val', ST_TwipsMeasure) - hRule = OptionalAttribute('w:hRule', WD_ROW_HEIGHT_RULE) - - -class CT_Row(BaseOxmlElement): - """ - ```` element - """ - tblPrEx = ZeroOrOne('w:tblPrEx') # custom inserter below - trPr = ZeroOrOne('w:trPr') # custom inserter below - tc = ZeroOrMore('w:tc') - - def tc_at_grid_col(self, idx): - """ - The ```` element appearing at grid column *idx*. Raises - |ValueError| if no ``w:tc`` element begins at that grid column. - """ - grid_col = 0 - for tc in self.tc_lst: - if grid_col == idx: - return tc - grid_col += tc.grid_span - if grid_col > idx: - raise ValueError('no cell on grid column %d' % idx) - raise ValueError('index out of bounds') - - @property - def tr_idx(self): - """ - The index of this ```` element within its parent ```` - element. - """ - return self.getparent().tr_lst.index(self) - - @property - def trHeight_hRule(self): - """ - Return the value of `w:trPr/w:trHeight@w:hRule`, or |None| if not - present. - """ - trPr = self.trPr - if trPr is None: - return None - return trPr.trHeight_hRule - - @trHeight_hRule.setter - def trHeight_hRule(self, value): - trPr = self.get_or_add_trPr() - trPr.trHeight_hRule = value - - @property - def trHeight_val(self): - """ - Return the value of `w:trPr/w:trHeight@w:val`, or |None| if not - present. - """ - trPr = self.trPr - if trPr is None: - return None - return trPr.trHeight_val - - @trHeight_val.setter - def trHeight_val(self, value): - trPr = self.get_or_add_trPr() - trPr.trHeight_val = value - - def _insert_tblPrEx(self, tblPrEx): - self.insert(0, tblPrEx) - - def _insert_trPr(self, trPr): - tblPrEx = self.tblPrEx - if tblPrEx is not None: - tblPrEx.addnext(trPr) - else: - self.insert(0, trPr) - - def _new_tc(self): - return CT_Tc.new() - - -class CT_Tbl(BaseOxmlElement): - """ - ```` element - """ - tblPr = OneAndOnlyOne('w:tblPr') - tblGrid = OneAndOnlyOne('w:tblGrid') - tr = ZeroOrMore('w:tr') - - @property - def bidiVisual_val(self): - """ - Value of `w:tblPr/w:bidiVisual/@w:val` or |None| if not present. - Controls whether table cells are displayed right-to-left or - left-to-right. - """ - bidiVisual = self.tblPr.bidiVisual - if bidiVisual is None: - return None - return bidiVisual.val - - @bidiVisual_val.setter - def bidiVisual_val(self, value): - tblPr = self.tblPr - if value is None: - tblPr._remove_bidiVisual() - else: - tblPr.get_or_add_bidiVisual().val = value - - @property - def col_count(self): - """ - The number of grid columns in this table. - """ - return len(self.tblGrid.gridCol_lst) - - def iter_tcs(self): - """ - Generate each of the `w:tc` elements in this table, left to right and - top to bottom. Each cell in the first row is generated, followed by - each cell in the second row, etc. - """ - for tr in self.tr_lst: - for tc in tr.tc_lst: - yield tc - - @classmethod - def new_tbl(cls, rows, cols, width): - """ - Return a new `w:tbl` element having *rows* rows and *cols* columns - with *width* distributed evenly between the columns. - """ - return parse_xml(cls._tbl_xml(rows, cols, width)) - - @property - def tblStyle_val(self): - """ - Value of `w:tblPr/w:tblStyle/@w:val` (a table style id) or |None| if - not present. - """ - tblStyle = self.tblPr.tblStyle - if tblStyle is None: - return None - return tblStyle.val - - @tblStyle_val.setter - def tblStyle_val(self, styleId): - """ - Set the value of `w:tblPr/w:tblStyle/@w:val` (a table style id) to - *styleId*. If *styleId* is None, remove the `w:tblStyle` element. - """ - tblPr = self.tblPr - tblPr._remove_tblStyle() - if styleId is None: - return - tblPr._add_tblStyle().val = styleId - - @classmethod - def _tbl_xml(cls, rows, cols, width): - col_width = Emu(width/cols) if cols > 0 else Emu(0) - return ( - '\n' - ' \n' - ' \n' - ' \n' - ' \n' - '%s' # tblGrid - '%s' # trs - '\n' - ) % ( - nsdecls('w'), - cls._tblGrid_xml(cols, col_width), - cls._trs_xml(rows, cols, col_width) - ) - - @classmethod - def _tblGrid_xml(cls, col_count, col_width): - xml = ' \n' - for i in range(col_count): - xml += ' \n' % col_width.twips - xml += ' \n' - return xml - - @classmethod - def _trs_xml(cls, row_count, col_count, col_width): - xml = '' - for i in range(row_count): - xml += ( - ' \n' - '%s' - ' \n' - ) % cls._tcs_xml(col_count, col_width) - return xml - - @classmethod - def _tcs_xml(cls, col_count, col_width): - xml = '' - for i in range(col_count): - xml += ( - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ) % col_width.twips - return xml - - -class CT_TblGrid(BaseOxmlElement): - """ - ```` element, child of ````, holds ```` - elements that define column count, width, etc. - """ - gridCol = ZeroOrMore('w:gridCol', successors=('w:tblGridChange',)) - - -class CT_TblGridCol(BaseOxmlElement): - """ - ```` element, child of ````, defines a table - column. - """ - w = OptionalAttribute('w:w', ST_TwipsMeasure) - - @property - def gridCol_idx(self): - """ - The index of this ```` element within its parent - ```` element. - """ - return self.getparent().gridCol_lst.index(self) - - -class CT_TblLayoutType(BaseOxmlElement): - """ - ```` element, specifying whether column widths are fixed or - can be automatically adjusted based on content. - """ - type = OptionalAttribute('w:type', ST_TblLayoutType) - -class CT_TblBoarders(BaseOxmlElement): - pass - -class CT_TblPr(BaseOxmlElement): - """ - ```` element, child of ````, holds child elements that - define table properties such as style and borders. - """ - _tag_seq = ( - 'w:tblStyle', 'w:tblpPr', 'w:tblOverlap', 'w:bidiVisual', - 'w:tblStyleRowBandSize', 'w:tblStyleColBandSize', 'w:tblW', 'w:jc', - 'w:tblCellSpacing', 'w:tblInd', 'w:tblBorders', 'w:shd', - 'w:tblLayout', 'w:tblCellMar', 'w:tblLook', 'w:tblCaption', - 'w:tblDescription', 'w:tblPrChange' - ) - tblStyle = ZeroOrOne('w:tblStyle', successors=_tag_seq[1:]) - bidiVisual = ZeroOrOne('w:bidiVisual', successors=_tag_seq[4:]) - tblW =ZeroOrOne ('w:tblW', successors=('w:tblPr',)) - tblCellMar = ZeroOrOne('w:tblCellMar', successors=('w:tblPr',)) - jc = ZeroOrOne('w:jc', successors=_tag_seq[8:]) - tblLayout = ZeroOrOne('w:tblLayout', successors=_tag_seq[13:]) - tblBorders = ZeroOrOne('w:tblBorders', successors=('w:tblPr',)) - del _tag_seq - - @property - def alignment(self): - """ - Member of :ref:`WdRowAlignment` enumeration or |None|, based on the - contents of the `w:val` attribute of `./w:jc`. |None| if no `w:jc` - element is present. - """ - jc = self.jc - if jc is None: - return None - return jc.val - - @alignment.setter - def alignment(self, value): - self._remove_jc() - if value is None: - return - jc = self.get_or_add_jc() - jc.val = value - - @property - def autofit(self): - """ - Return |False| if there is a ```` child with ``w:type`` - attribute set to ``'fixed'``. Otherwise return |True|. - """ - tblLayout = self.tblLayout - if tblLayout is None: - return True - return False if tblLayout.type == 'fixed' else True - - @autofit.setter - def autofit(self, value): - tblLayout = self.get_or_add_tblLayout() - tblLayout.type = 'autofit' if value else 'fixed' - - @property - def style(self): - """ - Return the value of the ``val`` attribute of the ```` - child or |None| if not present. - """ - tblStyle = self.tblStyle - if tblStyle is None: - return None - return tblStyle.val - - @style.setter - def style(self, value): - self._remove_tblStyle() - if value is None: - return - self._add_tblStyle(val=value) - - -class CT_TblWidth(BaseOxmlElement): - """ - Used for ```` and ```` elements and many others, to - specify a table-related width. - """ - # the type for `w` attr is actually ST_MeasurementOrPercent, but using - # XsdInt for now because only dxa (twips) values are being used. It's not - # entirely clear what the semantics are for other values like -01.4mm - w = RequiredAttribute('w:w', XsdInt) - type = RequiredAttribute('w:type', ST_TblWidth) - - @property - def width(self): - """ - Return the EMU length value represented by the combined ``w:w`` and - ``w:type`` attributes. - """ - if self.type != 'dxa': - return None - return Twips(self.w) - - @width.setter - def width(self, value): - self.type = 'dxa' - self.w = Emu(value).twips - - -class CT_Tc(BaseOxmlElement): - """ - ```` table cell element - """ - tcPr = ZeroOrOne('w:tcPr') # bunches of successors, overriding insert - p = OneOrMore('w:p') - tbl = OneOrMore('w:tbl') - - @property - def bottom(self): - """ - The row index that marks the bottom extent of the vertical span of - this cell. This is one greater than the index of the bottom-most row - of the span, similar to how a slice of the cell's rows would be - specified. - """ - if self.vMerge is not None: - tc_below = self._tc_below - if tc_below is not None and tc_below.vMerge == ST_Merge.CONTINUE: - return tc_below.bottom - return self._tr_idx + 1 - - def clear_content(self): - """ - Remove all content child elements, preserving the ```` - element if present. Note that this leaves the ```` element in - an invalid state because it doesn't contain at least one block-level - element. It's up to the caller to add a ````child element as the - last content element. - """ - new_children = [] - tcPr = self.tcPr - if tcPr is not None: - new_children.append(tcPr) - self[:] = new_children - - @property - def grid_span(self): - """ - The integer number of columns this cell spans. Determined by - ./w:tcPr/w:gridSpan/@val, it defaults to 1. - """ - tcPr = self.tcPr - if tcPr is None: - return 1 - return tcPr.grid_span - - @grid_span.setter - def grid_span(self, value): - tcPr = self.get_or_add_tcPr() - tcPr.grid_span = value - - def iter_block_items(self): - """ - Generate a reference to each of the block-level content elements in - this cell, in the order they appear. - """ - block_item_tags = (qn('w:p'), qn('w:tbl'), qn('w:sdt')) - for child in self: - if child.tag in block_item_tags: - yield child - - @property - def left(self): - """ - The grid column index at which this ```` element appears. - """ - return self._grid_col - - def merge(self, other_tc): - """ - Return the top-left ```` element of a new span formed by - merging the rectangular region defined by using this tc element and - *other_tc* as diagonal corners. - """ - top, left, height, width = self._span_dimensions(other_tc) - top_tc = self._tbl.tr_lst[top].tc_at_grid_col(left) - top_tc._grow_to(width, height) - return top_tc - - @classmethod - def new(cls): - """ - Return a new ```` element, containing an empty paragraph as the - required EG_BlockLevelElt. - """ - return parse_xml( - '\n' - ' \n' - '' % nsdecls('w') - ) - - @property - def right(self): - """ - The grid column index that marks the right-side extent of the - horizontal span of this cell. This is one greater than the index of - the right-most column of the span, similar to how a slice of the - cell's columns would be specified. - """ - return self._grid_col + self.grid_span - - @property - def top(self): - """ - The top-most row index in the vertical span of this cell. - """ - if self.vMerge is None or self.vMerge == ST_Merge.RESTART: - return self._tr_idx - return self._tc_above.top - - @property - def vMerge(self): - """ - The value of the ./w:tcPr/w:vMerge/@val attribute, or |None| if the - w:vMerge element is not present. - """ - tcPr = self.tcPr - if tcPr is None: - return None - return tcPr.vMerge_val - - @vMerge.setter - def vMerge(self, value): - tcPr = self.get_or_add_tcPr() - tcPr.vMerge_val = value - - @property - def width(self): - """ - Return the EMU length value represented in the ``./w:tcPr/w:tcW`` - child element or |None| if not present. - """ - tcPr = self.tcPr - if tcPr is None: - return None - return tcPr.width - - @width.setter - def width(self, value): - tcPr = self.get_or_add_tcPr() - tcPr.width = value - - def _add_width_of(self, other_tc): - """ - Add the width of *other_tc* to this cell. Does nothing if either this - tc or *other_tc* does not have a specified width. - """ - if self.width and other_tc.width: - self.width += other_tc.width - - @property - def _grid_col(self): - """ - The grid column at which this cell begins. - """ - tr = self._tr - idx = tr.tc_lst.index(self) - preceding_tcs = tr.tc_lst[:idx] - return sum(tc.grid_span for tc in preceding_tcs) - - def _grow_to(self, width, height, top_tc=None): - """ - Grow this cell to *width* grid columns and *height* rows by expanding - horizontal spans and creating continuation cells to form vertical - spans. - """ - def vMerge_val(top_tc): - if top_tc is not self: - return ST_Merge.CONTINUE - if height == 1: - return None - return ST_Merge.RESTART - - top_tc = self if top_tc is None else top_tc - self._span_to_width(width, top_tc, vMerge_val(top_tc)) - if height > 1: - self._tc_below._grow_to(width, height-1, top_tc) - - def _insert_tcPr(self, tcPr): - """ - ``tcPr`` has a bunch of successors, but it comes first if it appears, - so just overriding and using insert(0, ...) rather than spelling out - successors. - """ - self.insert(0, tcPr) - return tcPr - - @property - def _is_empty(self): - """ - True if this cell contains only a single empty ```` element. - """ - block_items = list(self.iter_block_items()) - if len(block_items) > 1: - return False - p = block_items[0] # cell must include at least one element - if len(p.r_lst) == 0: - return True - return False - - def _move_content_to(self, other_tc): - """ - Append the content of this cell to *other_tc*, leaving this cell with - a single empty ```` element. - """ - if other_tc is self: - return - if self._is_empty: - return - other_tc._remove_trailing_empty_p() - # appending moves each element from self to other_tc - for block_element in self.iter_block_items(): - other_tc.append(block_element) - # add back the required minimum single empty element - self.append(self._new_p()) - - def _new_tbl(self): - return CT_Tbl.new() - - @property - def _next_tc(self): - """ - The `w:tc` element immediately following this one in this row, or - |None| if this is the last `w:tc` element in the row. - """ - following_tcs = self.xpath('./following-sibling::w:tc') - return following_tcs[0] if following_tcs else None - - def _remove(self): - """ - Remove this `w:tc` element from the XML tree. - """ - self.getparent().remove(self) - - def _remove_trailing_empty_p(self): - """ - Remove the last content element from this cell if it is an empty - ```` element. - """ - block_items = list(self.iter_block_items()) - last_content_elm = block_items[-1] - if last_content_elm.tag != qn('w:p'): - return - p = last_content_elm - if len(p.r_lst) > 0: - return - self.remove(p) - - def _span_dimensions(self, other_tc): - """ - Return a (top, left, height, width) 4-tuple specifying the extents of - the merged cell formed by using this tc and *other_tc* as opposite - corner extents. - """ - def raise_on_inverted_L(a, b): - if a.top == b.top and a.bottom != b.bottom: - raise InvalidSpanError('requested span not rectangular') - if a.left == b.left and a.right != b.right: - raise InvalidSpanError('requested span not rectangular') - - def raise_on_tee_shaped(a, b): - top_most, other = (a, b) if a.top < b.top else (b, a) - if top_most.top < other.top and top_most.bottom > other.bottom: - raise InvalidSpanError('requested span not rectangular') - - left_most, other = (a, b) if a.left < b.left else (b, a) - if left_most.left < other.left and left_most.right > other.right: - raise InvalidSpanError('requested span not rectangular') - - raise_on_inverted_L(self, other_tc) - raise_on_tee_shaped(self, other_tc) - - top = min(self.top, other_tc.top) - left = min(self.left, other_tc.left) - bottom = max(self.bottom, other_tc.bottom) - right = max(self.right, other_tc.right) - - return top, left, bottom - top, right - left - - def _span_to_width(self, grid_width, top_tc, vMerge): - """ - Incorporate and then remove `w:tc` elements to the right of this one - until this cell spans *grid_width*. Raises |ValueError| if - *grid_width* cannot be exactly achieved, such as when a merged cell - would drive the span width greater than *grid_width* or if not enough - grid columns are available to make this cell that wide. All content - from incorporated cells is appended to *top_tc*. The val attribute of - the vMerge element on the single remaining cell is set to *vMerge*. - If *vMerge* is |None|, the vMerge element is removed if present. - """ - self._move_content_to(top_tc) - while self.grid_span < grid_width: - self._swallow_next_tc(grid_width, top_tc) - self.vMerge = vMerge - - def _swallow_next_tc(self, grid_width, top_tc): - """ - Extend the horizontal span of this `w:tc` element to incorporate the - following `w:tc` element in the row and then delete that following - `w:tc` element. Any content in the following `w:tc` element is - appended to the content of *top_tc*. The width of the following - `w:tc` element is added to this one, if present. Raises - |InvalidSpanError| if the width of the resulting cell is greater than - *grid_width* or if there is no next `` element in the row. - """ - def raise_on_invalid_swallow(next_tc): - if next_tc is None: - raise InvalidSpanError('not enough grid columns') - if self.grid_span + next_tc.grid_span > grid_width: - raise InvalidSpanError('span is not rectangular') - - next_tc = self._next_tc - raise_on_invalid_swallow(next_tc) - next_tc._move_content_to(top_tc) - self._add_width_of(next_tc) - self.grid_span += next_tc.grid_span - next_tc._remove() - - @property - def _tbl(self): - """ - The tbl element this tc element appears in. - """ - return self.xpath('./ancestor::w:tbl[position()=1]')[0] - - @property - def _tc_above(self): - """ - The `w:tc` element immediately above this one in its grid column. - """ - return self._tr_above.tc_at_grid_col(self._grid_col) - - @property - def _tc_below(self): - """ - The tc element immediately below this one in its grid column. - """ - tr_below = self._tr_below - if tr_below is None: - return None - return tr_below.tc_at_grid_col(self._grid_col) - - @property - def _tr(self): - """ - The tr element this tc element appears in. - """ - return self.xpath('./ancestor::w:tr[position()=1]')[0] - - @property - def _tr_above(self): - """ - The tr element prior in sequence to the tr this cell appears in. - Raises |ValueError| if called on a cell in the top-most row. - """ - tr_lst = self._tbl.tr_lst - tr_idx = tr_lst.index(self._tr) - if tr_idx == 0: - raise ValueError('no tr above topmost tr') - return tr_lst[tr_idx-1] - - @property - def _tr_below(self): - """ - The tr element next in sequence after the tr this cell appears in, or - |None| if this cell appears in the last row. - """ - tr_lst = self._tbl.tr_lst - tr_idx = tr_lst.index(self._tr) - try: - return tr_lst[tr_idx+1] - except IndexError: - return None - - @property - def _tr_idx(self): - """ - The row index of the tr element this tc element appears in. - """ - return self._tbl.tr_lst.index(self._tr) - -class CT_TcBorders(BaseOxmlElement): - """ - element - """ - top = ZeroOrOne('w:top') - start = ZeroOrOne('w:start') - bottom = ZeroOrOne('w:bottom',successors=('w:tblPr',) ) - end = ZeroOrOne('w:end') - - - def new(cls): - """ - Return a new ```` element - """ - return parse_xml( - '\n' - '' % nsdecls('w') - ) - - def add_bottom_border(self, val, sz): - bottom = CT_Bottom.new ( val, sz) - return self._insert_bottom(bottom) - - -class CT_TcPr(BaseOxmlElement): - """ - ```` element, defining table cell properties - """ - _tag_seq = ( - 'w:cnfStyle', 'w:tcW', 'w:gridSpan', 'w:hMerge', 'w:vMerge', - 'w:tcBorders', 'w:shd', 'w:noWrap', 'w:tcMar', 'w:textDirection', - 'w:tcFitText', 'w:vAlign', 'w:hideMark', 'w:headers', 'w:cellIns', - 'w:cellDel', 'w:cellMerge', 'w:tcPrChange' - ) - tcW = ZeroOrOne('w:tcW', successors=_tag_seq[2:]) - gridSpan = ZeroOrOne('w:gridSpan', successors=_tag_seq[3:]) - tcBorders = ZeroOrOne('w:tcBorders', successors = ('w:tcPr',)) - vMerge = ZeroOrOne('w:vMerge', successors=_tag_seq[5:]) - vAlign = ZeroOrOne('w:vAlign', successors=_tag_seq[12:]) - - del _tag_seq - - @property - def grid_span(self): - """ - The integer number of columns this cell spans. Determined by - ./w:gridSpan/@val, it defaults to 1. - """ - gridSpan = self.gridSpan - if gridSpan is None: - return 1 - return gridSpan.val - - @grid_span.setter - def grid_span(self, value): - self._remove_gridSpan() - if value > 1: - self.get_or_add_gridSpan().val = value - - @property - def vAlign_val(self): - """Value of `w:val` attribute on `w:vAlign` child. - - Value is |None| if `w:vAlign` child is not present. The `w:val` - attribute on `w:vAlign` is required. - """ - vAlign = self.vAlign - if vAlign is None: - return None - return vAlign.val - - @vAlign_val.setter - def vAlign_val(self, value): - if value is None: - self._remove_vAlign() - return - self.get_or_add_vAlign().val = value - - @property - def vMerge_val(self): - """ - The value of the ./w:vMerge/@val attribute, or |None| if the - w:vMerge element is not present. - """ - vMerge = self.vMerge - if vMerge is None: - return None - return vMerge.val - - @vMerge_val.setter - def vMerge_val(self, value): - self._remove_vMerge() - if value is not None: - self._add_vMerge().val = value - - @property - def width(self): - """ - Return the EMU length value represented in the ```` child - element or |None| if not present or its type is not 'dxa'. - """ - tcW = self.tcW - if tcW is None: - return None - return tcW.width - - @width.setter - def width(self, value): - tcW = self.get_or_add_tcW() - tcW.width = value - - -class CT_TrPr(BaseOxmlElement): - """ - ```` element, defining table row properties - """ - _tag_seq = ( - 'w:cnfStyle', 'w:divId', 'w:gridBefore', 'w:gridAfter', 'w:wBefore', - 'w:wAfter', 'w:cantSplit', 'w:trHeight', 'w:tblHeader', - 'w:tblCellSpacing', 'w:jc', 'w:hidden', 'w:ins', 'w:del', - 'w:trPrChange' - ) - trHeight = ZeroOrOne('w:trHeight', successors=_tag_seq[8:]) - del _tag_seq - - @property - def trHeight_hRule(self): - """ - Return the value of `w:trHeight@w:hRule`, or |None| if not present. - """ - trHeight = self.trHeight - if trHeight is None: - return None - return trHeight.hRule - - @trHeight_hRule.setter - def trHeight_hRule(self, value): - if value is None and self.trHeight is None: - return - trHeight = self.get_or_add_trHeight() - trHeight.hRule = value - - @property - def trHeight_val(self): - """ - Return the value of `w:trHeight@w:val`, or |None| if not present. - """ - trHeight = self.trHeight - if trHeight is None: - return None - return trHeight.val - - @trHeight_val.setter - def trHeight_val(self, value): - if value is None and self.trHeight is None: - return - trHeight = self.get_or_add_trHeight() - trHeight.val = value - - -class CT_VerticalJc(BaseOxmlElement): - """`w:vAlign` element, specifying vertical alignment of cell.""" - val = RequiredAttribute('w:val', WD_CELL_VERTICAL_ALIGNMENT) - - -class CT_VMerge(BaseOxmlElement): - """ - ```` element, specifying vertical merging behavior of a cell. - """ - val = OptionalAttribute('w:val', ST_Merge, default=ST_Merge.CONTINUE) - - -class CT_TblMar(BaseOxmlElement): - """ - ```` element - """ - left = ZeroOrOne('w:left', successors=('w:tblCellMar',)) - right = ZeroOrOne('w:write', successors=('w:tblCellMar',)) - - -class CT_Bottom(BaseOxmlElement): - """ - element - """ - val= OptionalAttribute('w:val', ST_String) - sz= OptionalAttribute('w:sz', ST_String) - space = OptionalAttribute('w:space', ST_String) - color = OptionalAttribute('w:color', ST_String) - - @classmethod - def new(cls, val, sz): - bottom = OxmlElement('w:bottom') - bottom.val = val - bottom.sz = sz - bottom.space = "0" - bottom.color = "auto" - - return bottom diff --git a/build/lib/docx/oxml/text/__init__.py b/build/lib/docx/oxml/text/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/lib/docx/oxml/text/font.py b/build/lib/docx/oxml/text/font.py deleted file mode 100644 index 810ec2b30..000000000 --- a/build/lib/docx/oxml/text/font.py +++ /dev/null @@ -1,320 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes related to run properties (font). -""" - -from .. import parse_xml -from ...enum.dml import MSO_THEME_COLOR -from ...enum.text import WD_COLOR, WD_UNDERLINE -from ..ns import nsdecls, qn -from ..simpletypes import ( - ST_HexColor, ST_HpsMeasure, ST_String, ST_VerticalAlignRun -) -from ..xmlchemy import ( - BaseOxmlElement, OptionalAttribute, RequiredAttribute, ZeroOrOne -) - - -class CT_Color(BaseOxmlElement): - """ - `w:color` element, specifying the color of a font and perhaps other - objects. - """ - val = RequiredAttribute('w:val', ST_HexColor) - themeColor = OptionalAttribute('w:themeColor', MSO_THEME_COLOR) - - -class CT_Fonts(BaseOxmlElement): - """ - ```` element, specifying typeface name for the various language - types. - """ - ascii = OptionalAttribute('w:ascii', ST_String) - hAnsi = OptionalAttribute('w:hAnsi', ST_String) - - -class CT_Highlight(BaseOxmlElement): - """ - `w:highlight` element, specifying font highlighting/background color. - """ - val = RequiredAttribute('w:val', WD_COLOR) - - -class CT_HpsMeasure(BaseOxmlElement): - """ - Used for ```` element and others, specifying font size in - half-points. - """ - val = RequiredAttribute('w:val', ST_HpsMeasure) - - -class CT_RPr(BaseOxmlElement): - """ - ```` element, containing the properties for a run. - """ - _tag_seq = ( - 'w:rStyle', 'w:rFonts', 'w:b', 'w:bCs', 'w:i', 'w:iCs', 'w:caps', - 'w:smallCaps', 'w:strike', 'w:dstrike', 'w:outline', 'w:shadow', - 'w:emboss', 'w:imprint', 'w:noProof', 'w:snapToGrid', 'w:vanish', - 'w:webHidden', 'w:color', 'w:spacing', 'w:w', 'w:kern', 'w:position', - 'w:sz', 'w:szCs', 'w:highlight', 'w:u', 'w:effect', 'w:bdr', 'w:shd', - 'w:fitText', 'w:vertAlign', 'w:rtl', 'w:cs', 'w:em', 'w:lang', - 'w:eastAsianLayout', 'w:specVanish', 'w:oMath' - ) - rStyle = ZeroOrOne('w:rStyle', successors=_tag_seq[1:]) - rFonts = ZeroOrOne('w:rFonts', successors=_tag_seq[2:]) - b = ZeroOrOne('w:b', successors=_tag_seq[3:]) - bCs = ZeroOrOne('w:bCs', successors=_tag_seq[4:]) - i = ZeroOrOne('w:i', successors=_tag_seq[5:]) - iCs = ZeroOrOne('w:iCs', successors=_tag_seq[6:]) - caps = ZeroOrOne('w:caps', successors=_tag_seq[7:]) - smallCaps = ZeroOrOne('w:smallCaps', successors=_tag_seq[8:]) - strike = ZeroOrOne('w:strike', successors=_tag_seq[9:]) - dstrike = ZeroOrOne('w:dstrike', successors=_tag_seq[10:]) - outline = ZeroOrOne('w:outline', successors=_tag_seq[11:]) - shadow = ZeroOrOne('w:shadow', successors=_tag_seq[12:]) - emboss = ZeroOrOne('w:emboss', successors=_tag_seq[13:]) - imprint = ZeroOrOne('w:imprint', successors=_tag_seq[14:]) - noProof = ZeroOrOne('w:noProof', successors=_tag_seq[15:]) - snapToGrid = ZeroOrOne('w:snapToGrid', successors=_tag_seq[16:]) - vanish = ZeroOrOne('w:vanish', successors=_tag_seq[17:]) - webHidden = ZeroOrOne('w:webHidden', successors=_tag_seq[18:]) - color = ZeroOrOne('w:color', successors=_tag_seq[19:]) - sz = ZeroOrOne('w:sz', successors=_tag_seq[24:]) - highlight = ZeroOrOne('w:highlight', successors=_tag_seq[26:]) - u = ZeroOrOne('w:u', successors=_tag_seq[27:]) - vertAlign = ZeroOrOne('w:vertAlign', successors=_tag_seq[32:]) - rtl = ZeroOrOne('w:rtl', successors=_tag_seq[33:]) - cs = ZeroOrOne('w:cs', successors=_tag_seq[34:]) - specVanish = ZeroOrOne('w:specVanish', successors=_tag_seq[38:]) - oMath = ZeroOrOne('w:oMath', successors=_tag_seq[39:]) - del _tag_seq - - def _new_color(self): - """ - Override metaclass method to set `w:color/@val` to RGB black on - create. - """ - return parse_xml('' % nsdecls('w')) - - @property - def highlight_val(self): - """ - Value of `w:highlight/@val` attribute, specifying a font's highlight - color, or `None` if the text is not highlighted. - """ - highlight = self.highlight - if highlight is None: - return None - return highlight.val - - @highlight_val.setter - def highlight_val(self, value): - if value is None: - self._remove_highlight() - return - highlight = self.get_or_add_highlight() - highlight.val = value - - @property - def rFonts_ascii(self): - """ - The value of `w:rFonts/@w:ascii` or |None| if not present. Represents - the assigned typeface name. The rFonts element also specifies other - special-case typeface names; this method handles the case where just - the common name is required. - """ - rFonts = self.rFonts - if rFonts is None: - return None - return rFonts.ascii - - @rFonts_ascii.setter - def rFonts_ascii(self, value): - if value is None: - self._remove_rFonts() - return - rFonts = self.get_or_add_rFonts() - rFonts.ascii = value - - @property - def rFonts_hAnsi(self): - """ - The value of `w:rFonts/@w:hAnsi` or |None| if not present. - """ - rFonts = self.rFonts - if rFonts is None: - return None - return rFonts.hAnsi - - @rFonts_hAnsi.setter - def rFonts_hAnsi(self, value): - if value is None and self.rFonts is None: - return - rFonts = self.get_or_add_rFonts() - rFonts.hAnsi = value - - @property - def style(self): - """ - String contained in child, or None if that element is not - present. - """ - rStyle = self.rStyle - if rStyle is None: - return None - return rStyle.val - - @style.setter - def style(self, style): - """ - Set val attribute of child element to *style*, adding a - new element if necessary. If *style* is |None|, remove the - element if present. - """ - if style is None: - self._remove_rStyle() - elif self.rStyle is None: - self._add_rStyle(val=style) - else: - self.rStyle.val = style - - @property - def subscript(self): - """ - |True| if `w:vertAlign/@w:val` is 'subscript'. |False| if - `w:vertAlign/@w:val` contains any other value. |None| if - `w:vertAlign` is not present. - """ - vertAlign = self.vertAlign - if vertAlign is None: - return None - if vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT: - return True - return False - - @subscript.setter - def subscript(self, value): - if value is None: - self._remove_vertAlign() - elif bool(value) is True: - self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUBSCRIPT - elif self.vertAlign is None: - return - elif self.vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT: - self._remove_vertAlign() - - @property - def superscript(self): - """ - |True| if `w:vertAlign/@w:val` is 'superscript'. |False| if - `w:vertAlign/@w:val` contains any other value. |None| if - `w:vertAlign` is not present. - """ - vertAlign = self.vertAlign - if vertAlign is None: - return None - if vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT: - return True - return False - - @superscript.setter - def superscript(self, value): - if value is None: - self._remove_vertAlign() - elif bool(value) is True: - self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUPERSCRIPT - elif self.vertAlign is None: - return - elif self.vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT: - self._remove_vertAlign() - - @property - def sz_val(self): - """ - The value of `w:sz/@w:val` or |None| if not present. - """ - sz = self.sz - if sz is None: - return None - return sz.val - - @sz_val.setter - def sz_val(self, value): - if value is None: - self._remove_sz() - return - sz = self.get_or_add_sz() - sz.val = value - - @property - def u_val(self): - """ - Value of `w:u/@val`, or None if not present. - """ - u = self.u - if u is None: - return None - return u.val - - @u_val.setter - def u_val(self, value): - self._remove_u() - if value is not None: - self._add_u().val = value - - def _get_bool_val(self, name): - """ - Return the value of the boolean child element having *name*, e.g. - 'b', 'i', and 'smallCaps'. - """ - element = getattr(self, name) - if element is None: - return None - return element.val - - def _set_bool_val(self, name, value): - if value is None: - getattr(self, '_remove_%s' % name)() - return - element = getattr(self, 'get_or_add_%s' % name)() - element.val = value - - -class CT_Underline(BaseOxmlElement): - """ - ```` element, specifying the underlining style for a run. - """ - @property - def val(self): - """ - The underline type corresponding to the ``w:val`` attribute value. - """ - val = self.get(qn('w:val')) - underline = WD_UNDERLINE.from_xml(val) - if underline == WD_UNDERLINE.SINGLE: - return True - if underline == WD_UNDERLINE.NONE: - return False - return underline - - @val.setter - def val(self, value): - # works fine without these two mappings, but only because True == 1 - # and False == 0, which happen to match the mapping for WD_UNDERLINE - # .SINGLE and .NONE respectively. - if value is True: - value = WD_UNDERLINE.SINGLE - elif value is False: - value = WD_UNDERLINE.NONE - - val = WD_UNDERLINE.to_xml(value) - self.set(qn('w:val'), val) - - -class CT_VerticalAlignRun(BaseOxmlElement): - """ - ```` element, specifying subscript or superscript. - """ - val = RequiredAttribute('w:val', ST_VerticalAlignRun) diff --git a/build/lib/docx/oxml/text/paragraph.py b/build/lib/docx/oxml/text/paragraph.py deleted file mode 100644 index 122b65c5f..000000000 --- a/build/lib/docx/oxml/text/paragraph.py +++ /dev/null @@ -1,135 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes related to paragraphs (CT_P). -""" - -from ..ns import qn -from ..xmlchemy import BaseOxmlElement, OxmlElement, ZeroOrMore, ZeroOrOne - - -class CT_P(BaseOxmlElement): - """ - ```` element, containing the properties and text for a paragraph. - """ - pPr = ZeroOrOne('w:pPr') - r = ZeroOrMore('w:r') - - def _insert_pPr(self, pPr): - self.insert(0, pPr) - return pPr - - def add_p_before(self): - """ - Return a new ```` element inserted directly prior to this one. - """ - new_p = OxmlElement('w:p') - self.addprevious(new_p) - return new_p - - def link_comment(self, _id, rangeStart=0, rangeEnd=0): - rStart = OxmlElement('w:commentRangeStart') - rStart._id = _id - rEnd = OxmlElement('w:commentRangeEnd') - rEnd._id = _id - if rangeStart == 0 and rangeEnd == 0: - self.insert(0,rStart) - self.append(rEnd) - else: - self.insert(rangeStart,rStart) - if rangeEnd == len(self.getchildren() ) - 1 : - self.append(rEnd) - else: - self.insert(rangeEnd+1, rEnd) - - def add_comm(self, author, comment_part, initials, dtime, comment_text, rangeStart, rangeEnd): - - comment = comment_part.add_comment(author, initials, dtime) - comment._add_p(comment_text) - _r = self.add_r() - _r.add_comment_reference(comment._id) - self.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) - - return comment - - def add_fn(self, text, footnotes_part): - footnote = footnotes_part.add_footnote() - footnote._add_p(' '+text) - _r = self.add_r() - _r.add_footnote_reference(footnote._id) - - return footnote - - def footnote_style(self): - pPr = self.get_or_add_pPr() - rstyle = pPr.get_or_add_pStyle() - rstyle.val = 'FootnoteText' - - return self - - @property - def alignment(self): - """ - The value of the ```` grandchild element or |None| if not - present. - """ - pPr = self.pPr - if pPr is None: - return None - return pPr.jc_val - - @alignment.setter - def alignment(self, value): - pPr = self.get_or_add_pPr() - pPr.jc_val = value - - def clear_content(self): - """ - Remove all child elements, except the ```` element if present. - """ - for child in self[:]: - if child.tag == qn('w:pPr'): - continue - self.remove(child) - - def set_sectPr(self, sectPr): - """ - Unconditionally replace or add *sectPr* as a grandchild in the - correct sequence. - """ - pPr = self.get_or_add_pPr() - pPr._remove_sectPr() - pPr._insert_sectPr(sectPr) - - @property - def style(self): - """ - String contained in w:val attribute of ./w:pPr/w:pStyle grandchild, - or |None| if not present. - """ - pPr = self.pPr - if pPr is None: - return None - return pPr.style - - @property - def comment_id(self): - _id = self.xpath('./w:commentRangeStart/@w:id') - if len(_id) > 1 or len(_id) == 0: - return None - else: - return int(_id[0]) - - @property - def footnote_ids(self): - _id = self.xpath('./w:r/w:footnoteReference/@w:id') - if len(_id) == 0 : - return None - else: - return _id - - - @style.setter - def style(self, style): - pPr = self.get_or_add_pPr() - pPr.style = style diff --git a/build/lib/docx/oxml/text/parfmt.py b/build/lib/docx/oxml/text/parfmt.py deleted file mode 100644 index 466b11b1b..000000000 --- a/build/lib/docx/oxml/text/parfmt.py +++ /dev/null @@ -1,348 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes related to paragraph properties (CT_PPr). -""" - -from ...enum.text import ( - WD_ALIGN_PARAGRAPH, WD_LINE_SPACING, WD_TAB_ALIGNMENT, WD_TAB_LEADER -) -from ...shared import Length -from ..simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure -from ..xmlchemy import ( - BaseOxmlElement, OneOrMore, OptionalAttribute, RequiredAttribute, - ZeroOrOne -) - - -class CT_Ind(BaseOxmlElement): - """ - ```` element, specifying paragraph indentation. - """ - left = OptionalAttribute('w:left', ST_SignedTwipsMeasure) - right = OptionalAttribute('w:right', ST_SignedTwipsMeasure) - firstLine = OptionalAttribute('w:firstLine', ST_TwipsMeasure) - hanging = OptionalAttribute('w:hanging', ST_TwipsMeasure) - - -class CT_Jc(BaseOxmlElement): - """ - ```` element, specifying paragraph justification. - """ - val = RequiredAttribute('w:val', WD_ALIGN_PARAGRAPH) - - -class CT_PPr(BaseOxmlElement): - """ - ```` element, containing the properties for a paragraph. - """ - _tag_seq = ( - 'w:pStyle', 'w:keepNext', 'w:keepLines', 'w:pageBreakBefore', - 'w:framePr', 'w:widowControl', 'w:numPr', 'w:suppressLineNumbers', - 'w:pBdr', 'w:shd', 'w:tabs', 'w:suppressAutoHyphens', 'w:kinsoku', - 'w:wordWrap', 'w:overflowPunct', 'w:topLinePunct', 'w:autoSpaceDE', - 'w:autoSpaceDN', 'w:bidi', 'w:adjustRightInd', 'w:snapToGrid', - 'w:spacing', 'w:ind', 'w:contextualSpacing', 'w:mirrorIndents', - 'w:suppressOverlap', 'w:jc', 'w:textDirection', 'w:textAlignment', - 'w:textboxTightWrap', 'w:outlineLvl', 'w:divId', 'w:cnfStyle', - 'w:rPr', 'w:sectPr', 'w:pPrChange' - ) - pStyle = ZeroOrOne('w:pStyle', successors=_tag_seq[1:]) - keepNext = ZeroOrOne('w:keepNext', successors=_tag_seq[2:]) - keepLines = ZeroOrOne('w:keepLines', successors=_tag_seq[3:]) - pageBreakBefore = ZeroOrOne('w:pageBreakBefore', successors=_tag_seq[4:]) - widowControl = ZeroOrOne('w:widowControl', successors=_tag_seq[6:]) - numPr = ZeroOrOne('w:numPr', successors=_tag_seq[7:]) - tabs = ZeroOrOne('w:tabs', successors=_tag_seq[11:]) - spacing = ZeroOrOne('w:spacing', successors=_tag_seq[22:]) - ind = ZeroOrOne('w:ind', successors=_tag_seq[23:]) - jc = ZeroOrOne('w:jc', successors=_tag_seq[27:]) - sectPr = ZeroOrOne('w:sectPr', successors=_tag_seq[35:]) - del _tag_seq - - @property - def first_line_indent(self): - """ - A |Length| value calculated from the values of `w:ind/@w:firstLine` - and `w:ind/@w:hanging`. Returns |None| if the `w:ind` child is not - present. - """ - ind = self.ind - if ind is None: - return None - hanging = ind.hanging - if hanging is not None: - return Length(-hanging) - firstLine = ind.firstLine - if firstLine is None: - return None - return firstLine - - @first_line_indent.setter - def first_line_indent(self, value): - if self.ind is None and value is None: - return - ind = self.get_or_add_ind() - ind.firstLine = ind.hanging = None - if value is None: - return - elif value < 0: - ind.hanging = -value - else: - ind.firstLine = value - - @property - def ind_left(self): - """ - The value of `w:ind/@w:left` or |None| if not present. - """ - ind = self.ind - if ind is None: - return None - return ind.left - - @ind_left.setter - def ind_left(self, value): - if value is None and self.ind is None: - return - ind = self.get_or_add_ind() - ind.left = value - - @property - def ind_right(self): - """ - The value of `w:ind/@w:right` or |None| if not present. - """ - ind = self.ind - if ind is None: - return None - return ind.right - - @ind_right.setter - def ind_right(self, value): - if value is None and self.ind is None: - return - ind = self.get_or_add_ind() - ind.right = value - - @property - def jc_val(self): - """ - The value of the ```` child element or |None| if not present. - """ - jc = self.jc - if jc is None: - return None - return jc.val - - @jc_val.setter - def jc_val(self, value): - if value is None: - self._remove_jc() - return - self.get_or_add_jc().val = value - - @property - def keepLines_val(self): - """ - The value of `keepLines/@val` or |None| if not present. - """ - keepLines = self.keepLines - if keepLines is None: - return None - return keepLines.val - - @keepLines_val.setter - def keepLines_val(self, value): - if value is None: - self._remove_keepLines() - else: - self.get_or_add_keepLines().val = value - - @property - def keepNext_val(self): - """ - The value of `keepNext/@val` or |None| if not present. - """ - keepNext = self.keepNext - if keepNext is None: - return None - return keepNext.val - - @keepNext_val.setter - def keepNext_val(self, value): - if value is None: - self._remove_keepNext() - else: - self.get_or_add_keepNext().val = value - - @property - def pageBreakBefore_val(self): - """ - The value of `pageBreakBefore/@val` or |None| if not present. - """ - pageBreakBefore = self.pageBreakBefore - if pageBreakBefore is None: - return None - return pageBreakBefore.val - - @pageBreakBefore_val.setter - def pageBreakBefore_val(self, value): - if value is None: - self._remove_pageBreakBefore() - else: - self.get_or_add_pageBreakBefore().val = value - - @property - def spacing_after(self): - """ - The value of `w:spacing/@w:after` or |None| if not present. - """ - spacing = self.spacing - if spacing is None: - return None - return spacing.after - - @spacing_after.setter - def spacing_after(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().after = value - - @property - def spacing_before(self): - """ - The value of `w:spacing/@w:before` or |None| if not present. - """ - spacing = self.spacing - if spacing is None: - return None - return spacing.before - - @spacing_before.setter - def spacing_before(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().before = value - - @property - def spacing_line(self): - """ - The value of `w:spacing/@w:line` or |None| if not present. - """ - spacing = self.spacing - if spacing is None: - return None - return spacing.line - - @spacing_line.setter - def spacing_line(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().line = value - - @property - def spacing_lineRule(self): - """ - The value of `w:spacing/@w:lineRule` as a member of the - :ref:`WdLineSpacing` enumeration. Only the `MULTIPLE`, `EXACTLY`, and - `AT_LEAST` members are used. It is the responsibility of the client - to calculate the use of `SINGLE`, `DOUBLE`, and `MULTIPLE` based on - the value of `w:spacing/@w:line` if that behavior is desired. - """ - spacing = self.spacing - if spacing is None: - return None - lineRule = spacing.lineRule - if lineRule is None and spacing.line is not None: - return WD_LINE_SPACING.MULTIPLE - return lineRule - - @spacing_lineRule.setter - def spacing_lineRule(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().lineRule = value - - @property - def style(self): - """ - String contained in child, or None if that element is not - present. - """ - pStyle = self.pStyle - if pStyle is None: - return None - return pStyle.val - - @style.setter - def style(self, style): - """ - Set val attribute of child element to *style*, adding a - new element if necessary. If *style* is |None|, remove the - element if present. - """ - if style is None: - self._remove_pStyle() - return - pStyle = self.get_or_add_pStyle() - pStyle.val = style - - @property - def widowControl_val(self): - """ - The value of `widowControl/@val` or |None| if not present. - """ - widowControl = self.widowControl - if widowControl is None: - return None - return widowControl.val - - @widowControl_val.setter - def widowControl_val(self, value): - if value is None: - self._remove_widowControl() - else: - self.get_or_add_widowControl().val = value - - -class CT_Spacing(BaseOxmlElement): - """ - ```` element, specifying paragraph spacing attributes such as - space before and line spacing. - """ - after = OptionalAttribute('w:after', ST_TwipsMeasure) - before = OptionalAttribute('w:before', ST_TwipsMeasure) - line = OptionalAttribute('w:line', ST_SignedTwipsMeasure) - lineRule = OptionalAttribute('w:lineRule', WD_LINE_SPACING) - - -class CT_TabStop(BaseOxmlElement): - """ - ```` element, representing an individual tab stop. - """ - val = RequiredAttribute('w:val', WD_TAB_ALIGNMENT) - leader = OptionalAttribute( - 'w:leader', WD_TAB_LEADER, default=WD_TAB_LEADER.SPACES - ) - pos = RequiredAttribute('w:pos', ST_SignedTwipsMeasure) - - -class CT_TabStops(BaseOxmlElement): - """ - ```` element, container for a sorted sequence of tab stops. - """ - tab = OneOrMore('w:tab', successors=()) - - def insert_tab_in_order(self, pos, align, leader): - """ - Insert a newly created `w:tab` child element in *pos* order. - """ - new_tab = self._new_tab() - new_tab.pos, new_tab.val, new_tab.leader = pos, align, leader - for tab in self.tab_lst: - if new_tab.pos < tab.pos: - tab.addprevious(new_tab) - return new_tab - self.append(new_tab) - return new_tab diff --git a/build/lib/docx/oxml/text/run.py b/build/lib/docx/oxml/text/run.py deleted file mode 100644 index a37709e96..000000000 --- a/build/lib/docx/oxml/text/run.py +++ /dev/null @@ -1,224 +0,0 @@ -# encoding: utf-8 - -""" -Custom element classes related to text runs (CT_R). -""" - -from ..ns import qn -from ..simpletypes import ST_BrClear, ST_BrType, ST_DecimalNumber, ST_String - -from .. import OxmlElement -from ..xmlchemy import ( - BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne ,RequiredAttribute -) - -from .. import OxmlElement - - -class CT_Br(BaseOxmlElement): - """ - ```` element, indicating a line, page, or column break in a run. - """ - type = OptionalAttribute('w:type', ST_BrType) - clear = OptionalAttribute('w:clear', ST_BrClear) - - -class CT_R(BaseOxmlElement): - """ - ```` element, containing the properties and text for a run. - """ - rPr = ZeroOrOne('w:rPr') - ###wrong - ref = ZeroOrOne('w:commentRangeStart', successors=('w:r',)) - t = ZeroOrMore('w:t') - br = ZeroOrMore('w:br') - cr = ZeroOrMore('w:cr') - tab = ZeroOrMore('w:tab') - drawing = ZeroOrMore('w:drawing') - - def _insert_rPr(self, rPr): - self.insert(0, rPr) - return rPr - - def add_t(self, text): - """ - Return a newly added ```` element containing *text*. - """ - t = self._add_t(text=text) - if len(text.strip()) < len(text): - t.set(qn('xml:space'), 'preserve') - return t - - def add_drawing(self, inline_or_anchor): - """ - Return a newly appended ``CT_Drawing`` (````) child - element having *inline_or_anchor* as its child. - """ - drawing = self._add_drawing() - drawing.append(inline_or_anchor) - return drawing - - def add_comment_reference(self, _id): - reference = OxmlElement('w:commentReference') - reference._id = _id - self.append(reference) - return reference - - def add_footnote_reference(self, _id): - rPr = self.get_or_add_rPr() - rstyle = rPr.get_or_add_rStyle() - rstyle.val = 'FootnoteReference' - reference = OxmlElement('w:footnoteReference') - reference._id = _id - self.append(reference) - return reference - - def add_footnoteRef(self): - ref = OxmlElement('w:footnoteRef') - self.append(ref) - - return ref - - def footnote_style(self): - rPr = self.get_or_add_rPr() - rstyle = rPr.get_or_add_rStyle() - rstyle.val = 'FootnoteReference' - - self.add_footnoteRef() - return self - - @property - def footnote_id(self): - _id = self.xpath('./w:footnoteReference/@w:id') - if len(_id) > 1 or len(_id) == 0 : - return None - else: - return int(_id[0]) - - def clear_content(self): - """ - Remove all child elements except the ```` element if present. - """ - content_child_elms = self[1:] if self.rPr is not None else self[:] - for child in content_child_elms: - self.remove(child) - - def add_comment_reference(self, _id): - reference = OxmlElement('w:commentReference') - reference._id = _id - self.append(reference) - return reference - - @property - def style(self): - """ - String contained in w:val attribute of grandchild, or - |None| if that element is not present. - """ - rPr = self.rPr - if rPr is None: - return None - return rPr.style - - @style.setter - def style(self, style): - """ - Set the character style of this element to *style*. If *style* - is None, remove the style element. - """ - rPr = self.get_or_add_rPr() - rPr.style = style - - @property - def text(self): - """ - A string representing the textual content of this run, with content - child elements like ```` translated to their Python - equivalent. - """ - text = '' - for child in self: - if child.tag == qn('w:t'): - t_text = child.text - text += t_text if t_text is not None else '' - elif child.tag == qn('w:tab'): - text += '\t' - elif child.tag in (qn('w:br'), qn('w:cr')): - text += '\n' - return text - - @text.setter - def text(self, text): - self.clear_content() - _RunContentAppender.append_to_run_from_text(self, text) - - -class CT_Text(BaseOxmlElement): - """ - ```` element, containing a sequence of characters within a run. - """ - - -class CT_RPr(BaseOxmlElement): - rStyle = ZeroOrOne('w:rStyle') - - -class CT_RStyle(BaseOxmlElement): - val = RequiredAttribute('w:val',ST_String) - -class _RunContentAppender(object): - """ - Service object that knows how to translate a Python string into run - content elements appended to a specified ```` element. Contiguous - sequences of regular characters are appended in a single ```` - element. Each tab character ('\t') causes a ```` element to be - appended. Likewise a newline or carriage return character ('\n', '\r') - causes a ```` element to be appended. - """ - def __init__(self, r): - self._r = r - self._bfr = [] - - @classmethod - def append_to_run_from_text(cls, r, text): - """ - Create a "one-shot" ``_RunContentAppender`` instance and use it to - append the run content elements corresponding to *text* to the - ```` element *r*. - """ - appender = cls(r) - appender.add_text(text) - - def add_text(self, text): - """ - Append the run content elements corresponding to *text* to the - ```` element of this instance. - """ - for char in text: - self.add_char(char) - self.flush() - - def add_char(self, char): - """ - Process the next character of input through the translation finite - state maching (FSM). There are two possible states, buffer pending - and not pending, but those are hidden behind the ``.flush()`` method - which must be called at the end of text to ensure any pending - ```` element is written. - """ - if char == '\t': - self.flush() - self._r.add_tab() - elif char in '\r\n': - self.flush() - self._r.add_br() - else: - self._bfr.append(char) - - def flush(self): - text = ''.join(self._bfr) - if text: - self._r.add_t(text) - del self._bfr[:] - - diff --git a/build/lib/docx/oxml/xmlchemy.py b/build/lib/docx/oxml/xmlchemy.py deleted file mode 100644 index 40df33494..000000000 --- a/build/lib/docx/oxml/xmlchemy.py +++ /dev/null @@ -1,761 +0,0 @@ -# encoding: utf-8 - -""" -Provides a wrapper around lxml that enables declarative definition of custom -element classes. -""" - -from __future__ import absolute_import - -from lxml import etree - -import re - -from . import OxmlElement -from ..compat import Unicode -from .exceptions import InvalidXmlError -from .ns import NamespacePrefixedTag, nsmap, qn -from ..shared import lazyproperty - - -def serialize_for_reading(element): - """ - Serialize *element* to human-readable XML suitable for tests. No XML - declaration. - """ - xml = etree.tostring(element, encoding='unicode', pretty_print=True) - return XmlString(xml) - - -class XmlString(Unicode): - """ - Provides string comparison override suitable for serialized XML that is - useful for tests. - """ - - # ' text' - # | | || | - # +----------+------------------------------------------++-----------+ - # front attrs | text - # close - - _xml_elm_line_patt = re.compile( - '( *)([^<]*)?$' - ) - - def __eq__(self, other): - lines = self.splitlines() - lines_other = other.splitlines() - if len(lines) != len(lines_other): - return False - for line, line_other in zip(lines, lines_other): - if not self._eq_elm_strs(line, line_other): - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def _attr_seq(self, attrs): - """ - Return a sequence of attribute strings parsed from *attrs*. Each - attribute string is stripped of whitespace on both ends. - """ - attrs = attrs.strip() - attr_lst = attrs.split() - return sorted(attr_lst) - - def _eq_elm_strs(self, line, line_2): - """ - Return True if the element in *line_2* is XML equivalent to the - element in *line*. - """ - front, attrs, close, text = self._parse_line(line) - front_2, attrs_2, close_2, text_2 = self._parse_line(line_2) - if front != front_2: - return False - if self._attr_seq(attrs) != self._attr_seq(attrs_2): - return False - if close != close_2: - return False - if text != text_2: - return False - return True - - @classmethod - def _parse_line(cls, line): - """ - Return front, attrs, close, text 4-tuple result of parsing XML element - string *line*. - """ - match = cls._xml_elm_line_patt.match(line) - front, attrs, close, text = [match.group(n) for n in range(1, 5)] - return front, attrs, close, text - - -class MetaOxmlElement(type): - """ - Metaclass for BaseOxmlElement - """ - def __init__(cls, clsname, bases, clsdict): - dispatchable = ( - OneAndOnlyOne, OneOrMore, OptionalAttribute, RequiredAttribute, - ZeroOrMore, ZeroOrOne, ZeroOrOneChoice - ) - for key, value in clsdict.items(): - if isinstance(value, dispatchable): - value.populate_class_members(cls, key) - - -class BaseAttribute(object): - """ - Base class for OptionalAttribute and RequiredAttribute, providing common - methods. - """ - def __init__(self, attr_name, simple_type): - super(BaseAttribute, self).__init__() - self._attr_name = attr_name - self._simple_type = simple_type - - def populate_class_members(self, element_cls, prop_name): - """ - Add the appropriate methods to *element_cls*. - """ - self._element_cls = element_cls - self._prop_name = prop_name - - self._add_attr_property() - - def _add_attr_property(self): - """ - Add a read/write ``{prop_name}`` property to the element class that - returns the interpreted value of this attribute on access and changes - the attribute value to its ST_* counterpart on assignment. - """ - property_ = property(self._getter, self._setter, None) - # assign unconditionally to overwrite element name definition - setattr(self._element_cls, self._prop_name, property_) - - @property - def _clark_name(self): - if ':' in self._attr_name: - return qn(self._attr_name) - return self._attr_name - - -class OptionalAttribute(BaseAttribute): - """ - Defines an optional attribute on a custom element class. An optional - attribute returns a default value when not present for reading. When - assigned |None|, the attribute is removed. - """ - def __init__(self, attr_name, simple_type, default=None): - super(OptionalAttribute, self).__init__(attr_name, simple_type) - self._default = default - - @property - def _getter(self): - """ - Return a function object suitable for the "get" side of the attribute - property descriptor. - """ - def get_attr_value(obj): - attr_str_value = obj.get(self._clark_name) - if attr_str_value is None: - return self._default - return self._simple_type.from_xml(attr_str_value) - get_attr_value.__doc__ = self._docstring - return get_attr_value - - @property - def _docstring(self): - """ - Return the string to use as the ``__doc__`` attribute of the property - for this attribute. - """ - return ( - '%s type-converted value of ``%s`` attribute, or |None| (or spec' - 'ified default value) if not present. Assigning the default valu' - 'e causes the attribute to be removed from the element.' % - (self._simple_type.__name__, self._attr_name) - ) - - @property - def _setter(self): - """ - Return a function object suitable for the "set" side of the attribute - property descriptor. - """ - def set_attr_value(obj, value): - if value is None or value == self._default: - if self._clark_name in obj.attrib: - del obj.attrib[self._clark_name] - return - str_value = self._simple_type.to_xml(value) - obj.set(self._clark_name, str_value) - return set_attr_value - - -class RequiredAttribute(BaseAttribute): - """ - Defines a required attribute on a custom element class. A required - attribute is assumed to be present for reading, so does not have - a default value; its actual value is always used. If missing on read, - an |InvalidXmlError| is raised. It also does not remove the attribute if - |None| is assigned. Assigning |None| raises |TypeError| or |ValueError|, - depending on the simple type of the attribute. - """ - @property - def _getter(self): - """ - Return a function object suitable for the "get" side of the attribute - property descriptor. - """ - def get_attr_value(obj): - attr_str_value = obj.get(self._clark_name) - if attr_str_value is None: - raise InvalidXmlError( - "required '%s' attribute not present on element %s" % - (self._attr_name, obj.tag) - ) - return self._simple_type.from_xml(attr_str_value) - get_attr_value.__doc__ = self._docstring - return get_attr_value - - @property - def _docstring(self): - """ - Return the string to use as the ``__doc__`` attribute of the property - for this attribute. - """ - return ( - '%s type-converted value of ``%s`` attribute.' % - (self._simple_type.__name__, self._attr_name) - ) - - @property - def _setter(self): - """ - Return a function object suitable for the "set" side of the attribute - property descriptor. - """ - def set_attr_value(obj, value): - str_value = self._simple_type.to_xml(value) - obj.set(self._clark_name, str_value) - return set_attr_value - - -class _BaseChildElement(object): - """ - Base class for the child element classes corresponding to varying - cardinalities, such as ZeroOrOne and ZeroOrMore. - """ - def __init__(self, nsptagname, successors=()): - super(_BaseChildElement, self).__init__() - self._nsptagname = nsptagname - self._successors = successors - - def populate_class_members(self, element_cls, prop_name): - """ - Baseline behavior for adding the appropriate methods to - *element_cls*. - """ - self._element_cls = element_cls - self._prop_name = prop_name - - def _add_adder(self): - """ - Add an ``_add_x()`` method to the element class for this child - element. - """ - def _add_child(obj, **attrs): - new_method = getattr(obj, self._new_method_name) - child = new_method() - for key, value in attrs.items(): - setattr(child, key, value) - insert_method = getattr(obj, self._insert_method_name) - insert_method(child) - return child - - _add_child.__doc__ = ( - 'Add a new ``<%s>`` child element unconditionally, inserted in t' - 'he correct sequence.' % self._nsptagname - ) - self._add_to_class(self._add_method_name, _add_child) - - def _add_creator(self): - """ - Add a ``_new_{prop_name}()`` method to the element class that creates - a new, empty element of the correct type, having no attributes. - """ - creator = self._creator - creator.__doc__ = ( - 'Return a "loose", newly created ``<%s>`` element having no attri' - 'butes, text, or children.' % self._nsptagname - ) - self._add_to_class(self._new_method_name, creator) - - def _add_getter(self): - """ - Add a read-only ``{prop_name}`` property to the element class for - this child element. - """ - property_ = property(self._getter, None, None) - # assign unconditionally to overwrite element name definition - setattr(self._element_cls, self._prop_name, property_) - - def _add_inserter(self): - """ - Add an ``_insert_x()`` method to the element class for this child - element. - """ - def _insert_child(obj, child): - obj.insert_element_before(child, *self._successors) - return child - - _insert_child.__doc__ = ( - 'Return the passed ``<%s>`` element after inserting it as a chil' - 'd in the correct sequence.' % self._nsptagname - ) - self._add_to_class(self._insert_method_name, _insert_child) - - def _add_list_getter(self): - """ - Add a read-only ``{prop_name}_lst`` property to the element class to - retrieve a list of child elements matching this type. - """ - prop_name = '%s_lst' % self._prop_name - property_ = property(self._list_getter, None, None) - setattr(self._element_cls, prop_name, property_) - - @lazyproperty - def _add_method_name(self): - return '_add_%s' % self._prop_name - - def _add_public_adder(self): - """ - Add a public ``add_x()`` method to the parent element class. - """ - def add_child(obj): - private_add_method = getattr(obj, self._add_method_name) - child = private_add_method() - return child - - add_child.__doc__ = ( - 'Add a new ``<%s>`` child element unconditionally, inserted in t' - 'he correct sequence.' % self._nsptagname - ) - self._add_to_class(self._public_add_method_name, add_child) - - def _add_to_class(self, name, method): - """ - Add *method* to the target class as *name*, unless *name* is already - defined on the class. - """ - if hasattr(self._element_cls, name): - return - setattr(self._element_cls, name, method) - - @property - def _creator(self): - """ - Return a function object that creates a new, empty element of the - right type, having no attributes. - """ - def new_child_element(obj): - return OxmlElement(self._nsptagname) - return new_child_element - - @property - def _getter(self): - """ - Return a function object suitable for the "get" side of the property - descriptor. This default getter returns the child element with - matching tag name or |None| if not present. - """ - def get_child_element(obj): - return obj.find(qn(self._nsptagname)) - get_child_element.__doc__ = ( - '``<%s>`` child element or |None| if not present.' - % self._nsptagname - ) - return get_child_element - - @lazyproperty - def _insert_method_name(self): - return '_insert_%s' % self._prop_name - - @property - def _list_getter(self): - """ - Return a function object suitable for the "get" side of a list - property descriptor. - """ - def get_child_element_list(obj): - return obj.findall(qn(self._nsptagname)) - get_child_element_list.__doc__ = ( - 'A list containing each of the ``<%s>`` child elements, in the o' - 'rder they appear.' % self._nsptagname - ) - return get_child_element_list - - @lazyproperty - def _public_add_method_name(self): - """ - add_childElement() is public API for a repeating element, allowing - new elements to be added to the sequence. May be overridden to - provide a friendlier API to clients having domain appropriate - parameter names for required attributes. - """ - return 'add_%s' % self._prop_name - - @lazyproperty - def _remove_method_name(self): - return '_remove_%s' % self._prop_name - - @lazyproperty - def _new_method_name(self): - return '_new_%s' % self._prop_name - - -class Choice(_BaseChildElement): - """ - Defines a child element belonging to a group, only one of which may - appear as a child. - """ - @property - def nsptagname(self): - return self._nsptagname - - def populate_class_members( - self, element_cls, group_prop_name, successors): - """ - Add the appropriate methods to *element_cls*. - """ - self._element_cls = element_cls - self._group_prop_name = group_prop_name - self._successors = successors - - self._add_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_get_or_change_to_method() - - def _add_get_or_change_to_method(self): - """ - Add a ``get_or_change_to_x()`` method to the element class for this - child element. - """ - def get_or_change_to_child(obj): - child = getattr(obj, self._prop_name) - if child is not None: - return child - remove_group_method = getattr( - obj, self._remove_group_method_name - ) - remove_group_method() - add_method = getattr(obj, self._add_method_name) - child = add_method() - return child - - get_or_change_to_child.__doc__ = ( - 'Return the ``<%s>`` child, replacing any other group element if' - ' found.' - ) % self._nsptagname - self._add_to_class( - self._get_or_change_to_method_name, get_or_change_to_child - ) - - @property - def _prop_name(self): - """ - Calculate property name from tag name, e.g. a:schemeClr -> schemeClr. - """ - if ':' in self._nsptagname: - start = self._nsptagname.index(':')+1 - else: - start = 0 - return self._nsptagname[start:] - - @lazyproperty - def _get_or_change_to_method_name(self): - return 'get_or_change_to_%s' % self._prop_name - - @lazyproperty - def _remove_group_method_name(self): - return '_remove_%s' % self._group_prop_name - - -class OneAndOnlyOne(_BaseChildElement): - """ - Defines a required child element for MetaOxmlElement. - """ - def __init__(self, nsptagname): - super(OneAndOnlyOne, self).__init__(nsptagname, None) - - def populate_class_members(self, element_cls, prop_name): - """ - Add the appropriate methods to *element_cls*. - """ - super(OneAndOnlyOne, self).populate_class_members( - element_cls, prop_name - ) - self._add_getter() - - @property - def _getter(self): - """ - Return a function object suitable for the "get" side of the property - descriptor. - """ - def get_child_element(obj): - child = obj.find(qn(self._nsptagname)) - if child is None: - raise InvalidXmlError( - "required ``<%s>`` child element not present" % - self._nsptagname - ) - return child - - get_child_element.__doc__ = ( - 'Required ``<%s>`` child element.' - % self._nsptagname - ) - return get_child_element - - -class OneOrMore(_BaseChildElement): - """ - Defines a repeating child element for MetaOxmlElement that must appear at - least once. - """ - def populate_class_members(self, element_cls, prop_name): - """ - Add the appropriate methods to *element_cls*. - """ - super(OneOrMore, self).populate_class_members( - element_cls, prop_name - ) - self._add_list_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_public_adder() - delattr(element_cls, prop_name) - - -class ZeroOrMore(_BaseChildElement): - """ - Defines an optional repeating child element for MetaOxmlElement. - """ - def populate_class_members(self, element_cls, prop_name): - """ - Add the appropriate methods to *element_cls*. - """ - super(ZeroOrMore, self).populate_class_members( - element_cls, prop_name - ) - self._add_list_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_public_adder() - delattr(element_cls, prop_name) - - -class ZeroOrOne(_BaseChildElement): - """ - Defines an optional child element for MetaOxmlElement. - """ - def populate_class_members(self, element_cls, prop_name): - """ - Add the appropriate methods to *element_cls*. - """ - super(ZeroOrOne, self).populate_class_members(element_cls, prop_name) - self._add_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_get_or_adder() - self._add_remover() - - def _add_get_or_adder(self): - """ - Add a ``get_or_add_x()`` method to the element class for this - child element. - """ - def get_or_add_child(obj): - child = getattr(obj, self._prop_name) - if child is None: - add_method = getattr(obj, self._add_method_name) - child = add_method() - return child - get_or_add_child.__doc__ = ( - 'Return the ``<%s>`` child element, newly added if not present.' - ) % self._nsptagname - self._add_to_class(self._get_or_add_method_name, get_or_add_child) - - def _add_remover(self): - """ - Add a ``_remove_x()`` method to the element class for this child - element. - """ - def _remove_child(obj): - obj.remove_all(self._nsptagname) - _remove_child.__doc__ = ( - 'Remove all ``<%s>`` child elements.' - ) % self._nsptagname - self._add_to_class(self._remove_method_name, _remove_child) - - @lazyproperty - def _get_or_add_method_name(self): - return 'get_or_add_%s' % self._prop_name - - -class ZeroOrOneChoice(_BaseChildElement): - """ - Correspondes to an ``EG_*`` element group where at most one of its - members may appear as a child. - """ - def __init__(self, choices, successors=()): - self._choices = choices - self._successors = successors - - def populate_class_members(self, element_cls, prop_name): - """ - Add the appropriate methods to *element_cls*. - """ - super(ZeroOrOneChoice, self).populate_class_members( - element_cls, prop_name - ) - self._add_choice_getter() - for choice in self._choices: - choice.populate_class_members( - element_cls, self._prop_name, self._successors - ) - self._add_group_remover() - - def _add_choice_getter(self): - """ - Add a read-only ``{prop_name}`` property to the element class that - returns the present member of this group, or |None| if none are - present. - """ - property_ = property(self._choice_getter, None, None) - # assign unconditionally to overwrite element name definition - setattr(self._element_cls, self._prop_name, property_) - - def _add_group_remover(self): - """ - Add a ``_remove_eg_x()`` method to the element class for this choice - group. - """ - def _remove_choice_group(obj): - for tagname in self._member_nsptagnames: - obj.remove_all(tagname) - - _remove_choice_group.__doc__ = ( - 'Remove the current choice group child element if present.' - ) - self._add_to_class( - self._remove_choice_group_method_name, _remove_choice_group - ) - - @property - def _choice_getter(self): - """ - Return a function object suitable for the "get" side of the property - descriptor. - """ - def get_group_member_element(obj): - return obj.first_child_found_in(*self._member_nsptagnames) - get_group_member_element.__doc__ = ( - 'Return the child element belonging to this element group, or ' - '|None| if no member child is present.' - ) - return get_group_member_element - - @lazyproperty - def _member_nsptagnames(self): - """ - Sequence of namespace-prefixed tagnames, one for each of the member - elements of this choice group. - """ - return [choice.nsptagname for choice in self._choices] - - @lazyproperty - def _remove_choice_group_method_name(self): - return '_remove_%s' % self._prop_name - - -class _OxmlElementBase(etree.ElementBase): - """ - Effective base class for all custom element classes, to add standardized - behavior to all classes in one place. Actual inheritance is from - BaseOxmlElement below, needed to manage Python 2-3 metaclass declaration - compatibility. - """ - - __metaclass__ = MetaOxmlElement - - def __repr__(self): - return "<%s '<%s>' at 0x%0x>" % ( - self.__class__.__name__, self._nsptag, id(self) - ) - - def first_child_found_in(self, *tagnames): - """ - Return the first child found with tag in *tagnames*, or None if - not found. - """ - for tagname in tagnames: - child = self.find(qn(tagname)) - if child is not None: - return child - return None - - def insert_element_before(self, elm, *tagnames): - successor = self.first_child_found_in(*tagnames) - if successor is not None: - successor.addprevious(elm) - else: - self.append(elm) - return elm - - def remove_all(self, *tagnames): - """ - Remove all child elements whose tagname (e.g. 'a:p') appears in - *tagnames*. - """ - for tagname in tagnames: - matching = self.findall(qn(tagname)) - for child in matching: - self.remove(child) - - @property - def xml(self): - """ - Return XML string for this element, suitable for testing purposes. - Pretty printed for readability and without an XML declaration at the - top. - """ - return serialize_for_reading(self) - - def xpath(self, xpath_str): - """ - Override of ``lxml`` _Element.xpath() method to provide standard Open - XML namespace mapping (``nsmap``) in centralized location. - """ - return super(BaseOxmlElement, self).xpath( - xpath_str, namespaces=nsmap - ) - - @property - def _nsptag(self): - return NamespacePrefixedTag.from_clark_name(self.tag) - - -BaseOxmlElement = MetaOxmlElement( - 'BaseOxmlElement', (etree.ElementBase,), dict(_OxmlElementBase.__dict__) -) diff --git a/build/lib/docx/package.py b/build/lib/docx/package.py deleted file mode 100644 index 4c9a6f6a1..000000000 --- a/build/lib/docx/package.py +++ /dev/null @@ -1,115 +0,0 @@ -# encoding: utf-8 - -""" -WordprocessingML Package class and related objects -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from docx.image.image import Image -from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.opc.package import OpcPackage -from docx.opc.packuri import PackURI -from docx.parts.image import ImagePart -from docx.shared import lazyproperty - - -class Package(OpcPackage): - """ - Customizations specific to a WordprocessingML package. - """ - def after_unmarshal(self): - """ - Called by loading code after all parts and relationships have been - loaded, to afford the opportunity for any required post-processing. - """ - self._gather_image_parts() - - @lazyproperty - def image_parts(self): - """ - Collection of all image parts in this package. - """ - return ImageParts() - - def _gather_image_parts(self): - """ - Load the image part collection with all the image parts in package. - """ - for rel in self.iter_rels(): - if rel.is_external: - continue - if rel.reltype != RT.IMAGE: - continue - if rel.target_part in self.image_parts: - continue - self.image_parts.append(rel.target_part) - - -class ImageParts(object): - """ - Collection of |ImagePart| instances corresponding to each image part in - the package. - """ - def __init__(self): - super(ImageParts, self).__init__() - self._image_parts = [] - - def __contains__(self, item): - return self._image_parts.__contains__(item) - - def __iter__(self): - return self._image_parts.__iter__() - - def __len__(self): - return self._image_parts.__len__() - - def append(self, item): - self._image_parts.append(item) - - def get_or_add_image_part(self, image_descriptor): - """ - Return an |ImagePart| instance containing the image identified by - *image_descriptor*, newly created if a matching one is not present in - the collection. - """ - image = Image.from_file(image_descriptor) - matching_image_part = self._get_by_sha1(image.sha1) - if matching_image_part is not None: - return matching_image_part - return self._add_image_part(image) - - def _add_image_part(self, image): - """ - Return an |ImagePart| instance newly created from image and appended - to the collection. - """ - partname = self._next_image_partname(image.ext) - image_part = ImagePart.from_image(image, partname) - self.append(image_part) - return image_part - - def _get_by_sha1(self, sha1): - """ - Return the image part in this collection having a SHA1 hash matching - *sha1*, or |None| if not found. - """ - for image_part in self._image_parts: - if image_part.sha1 == sha1: - return image_part - return None - - def _next_image_partname(self, ext): - """ - The next available image partname, starting from - ``/word/media/image1.{ext}`` where unused numbers are reused. The - partname is unique by number, without regard to the extension. *ext* - does not include the leading period. - """ - def image_partname(n): - return PackURI('/word/media/image%d.%s' % (n, ext)) - used_numbers = [image_part.partname.idx for image_part in self] - for n in range(1, len(self)+1): - if n not in used_numbers: - return image_partname(n) - return image_partname(len(self)+1) diff --git a/build/lib/docx/parts/__init__.py b/build/lib/docx/parts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/lib/docx/parts/comments.py b/build/lib/docx/parts/comments.py deleted file mode 100644 index 03a045aea..000000000 --- a/build/lib/docx/parts/comments.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import os - -from docx.opc.constants import CONTENT_TYPE as CT -from ..opc.packuri import PackURI - -from docx.oxml import parse_xml -from ..opc.part import XmlPart - -class CommentsPart(XmlPart): - """Definition of Comments Part""" - - @classmethod - def default(cls, package): - partname = PackURI("/word/comments.xml") - content_type = CT.WML_COMMENTS - element = parse_xml(cls._default_comments_xml()) - return cls(partname, content_type, element, package) - - @classmethod - def _default_comments_xml(cls): - path = os.path.join(os.path.split(__file__)[0], '..', 'templates', 'default-comments.xml') - with open(path, 'rb') as f: - xml_bytes = f.read() - return xml_bytes diff --git a/build/lib/docx/parts/document.py b/build/lib/docx/parts/document.py deleted file mode 100644 index 647f72119..000000000 --- a/build/lib/docx/parts/document.py +++ /dev/null @@ -1,206 +0,0 @@ -# encoding: utf-8 - -""" -|DocumentPart| and closely related objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..document import Document -from .numbering import NumberingPart -from ..opc.constants import RELATIONSHIP_TYPE as RT -from ..opc.part import XmlPart -from ..oxml.shape import CT_Inline -from ..shape import InlineShapes -from ..shared import lazyproperty -from .settings import SettingsPart -from .styles import StylesPart -from .comments import CommentsPart -from .footnotes import FootnotesPart - - -class DocumentPart(XmlPart): - """ - Main document part of a WordprocessingML (WML) package, aka a .docx file. - Acts as broker to other parts such as image, core properties, and style - parts. It also acts as a convenient delegate when a mid-document object - needs a service involving a remote ancestor. The `Parented.part` property - inherited by many content objects provides access to this part object for - that purpose. - """ - @property - def core_properties(self): - """ - A |CoreProperties| object providing read/write access to the core - properties of this document. - """ - return self.package.core_properties - - @property - def document(self): - """ - A |Document| object providing access to the content of this document. - """ - return Document(self._element, self) - - def get_or_add_image(self, image_descriptor): - """ - Return an (rId, image) 2-tuple for the image identified by - *image_descriptor*. *image* is an |Image| instance providing access - to the properties of the image, such as dimensions and image type. - *rId* is the key for the relationship between this document part and - the image part, reused if already present, newly created if not. - """ - image_part = self._package.image_parts.get_or_add_image_part( - image_descriptor - ) - rId = self.relate_to(image_part, RT.IMAGE) - return rId, image_part.image - - def get_style(self, style_id, style_type): - """ - Return the style in this document matching *style_id*. Returns the - default style for *style_type* if *style_id* is |None| or does not - match a defined style of *style_type*. - """ - return self.styles.get_by_id(style_id, style_type) - - def get_style_id(self, style_or_name, style_type): - """ - Return the style_id (|str|) of the style of *style_type* matching - *style_or_name*. Returns |None| if the style resolves to the default - style for *style_type* or if *style_or_name* is itself |None|. Raises - if *style_or_name* is a style of the wrong type or names a style not - present in the document. - """ - return self.styles.get_style_id(style_or_name, style_type) - - @lazyproperty - def inline_shapes(self): - """ - The |InlineShapes| instance containing the inline shapes in the - document. - """ - return InlineShapes(self._element.body, self) - - def new_pic_inline(self, image_descriptor, width, height): - """ - Return a newly-created `w:inline` element containing the image - specified by *image_descriptor* and scaled based on the values of - *width* and *height*. - """ - rId, image = self.get_or_add_image(image_descriptor) - cx, cy = image.scaled_dimensions(width, height) - shape_id, filename = self.next_id, image.filename - return CT_Inline.new_pic_inline(shape_id, rId, filename, cx, cy) - - @property - def next_id(self): - """Next available positive integer id value in this document. - - Calculated by incrementing maximum existing id value. Gaps in the - existing id sequence are not filled. The id attribute value is unique - in the document, without regard to the element type it appears on. - """ - id_str_lst = self._element.xpath('//@id') - used_ids = [int(id_str) for id_str in id_str_lst if id_str.isdigit()] - if not used_ids: - return 1 - return max(used_ids) + 1 - - @lazyproperty - def numbering_part(self): - """ - A |NumberingPart| object providing access to the numbering - definitions for this document. Creates an empty numbering part if one - is not present. - """ - try: - return self.part_related_by(RT.NUMBERING) - except KeyError: - numbering_part = NumberingPart.new() - self.relate_to(numbering_part, RT.NUMBERING) - return numbering_part - - - - def save(self, path_or_stream): - """ - Save this document to *path_or_stream*, which can be either a path to - a filesystem location (a string) or a file-like object. - """ - self.package.save(path_or_stream) - - @property - def settings(self): - """ - A |Settings| object providing access to the settings in the settings - part of this document. - """ - return self._settings_part.settings - - @property - def styles(self): - """ - A |Styles| object providing access to the styles in the styles part - of this document. - """ - return self._styles_part.styles - - @property - def _settings_part(self): - """ - A |SettingsPart| object providing access to the document-level - settings for this document. Creates a default settings part if one is - not present. - """ - try: - return self.part_related_by(RT.SETTINGS) - except KeyError: - settings_part = SettingsPart.default(self.package) - self.relate_to(settings_part, RT.SETTINGS) - return settings_part - - @property - def _styles_part(self): - """ - Instance of |StylesPart| for this document. Creates an empty styles - part if one is not present. - """ - try: - return self.part_related_by(RT.STYLES) - except KeyError: - styles_part = StylesPart.default(self.package) - self.relate_to(styles_part, RT.STYLES) - return styles_part - @lazyproperty - def comments_part(self): - """ - A |Comments| object providing read/write access to the core - properties of this document. - """ - # return self.package._comments_part - - @property - def _comments_part(self): - try: - return self.part_related_by(RT.COMMENTS) - except KeyError: - comments_part = CommentsPart.default(self) - self.relate_to(comments_part, RT.COMMENTS) - return comments_part - - @property - def _footnotes_part(self): - """ - |FootnotesPart| object related to this package. Creates - a default Comments part if one is not present. - """ - try: - return self.part_related_by(RT.FOOTNOTES) - except KeyError: - footnotes_part = FootnotesPart.default(self) - self.relate_to(footnotes_part, RT.FOOTNOTES) - return footnotes_part \ No newline at end of file diff --git a/build/lib/docx/parts/footnotes.py b/build/lib/docx/parts/footnotes.py deleted file mode 100644 index 67f29fb71..000000000 --- a/build/lib/docx/parts/footnotes.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from ..opc.constants import CONTENT_TYPE as CT -from ..opc.packuri import PackURI -from ..opc.part import XmlPart -from ..oxml import parse_xml - -import os - -class FootnotesPart(XmlPart): - """ - Definition of Footnotes Part - """ - @classmethod - def default(cls, package): - partname = PackURI("/word/footnotes.xml") - content_type = CT.WML_FOOTNOTES - element = parse_xml(cls._default_footnotes_xml()) - return cls(partname, content_type, element, package) - - @classmethod - def _default_footnotes_xml(cls): - path = os.path.join(os.path.split(__file__)[0], '..', 'templates', 'default-footnotes.xml') - with open(path, 'rb') as f: - xml_bytes = f.read() - return xml_bytes \ No newline at end of file diff --git a/build/lib/docx/parts/image.py b/build/lib/docx/parts/image.py deleted file mode 100644 index 6ece20d80..000000000 --- a/build/lib/docx/parts/image.py +++ /dev/null @@ -1,89 +0,0 @@ -# encoding: utf-8 - -""" -The proxy class for an image part, and related objects. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -import hashlib - -from docx.image.image import Image -from docx.opc.part import Part -from docx.shared import Emu, Inches - - -class ImagePart(Part): - """ - An image part. Corresponds to the target part of a relationship with type - RELATIONSHIP_TYPE.IMAGE. - """ - def __init__(self, partname, content_type, blob, image=None): - super(ImagePart, self).__init__(partname, content_type, blob) - self._image = image - - @property - def default_cx(self): - """ - Native width of this image, calculated from its width in pixels and - horizontal dots per inch (dpi). - """ - px_width = self.image.px_width - horz_dpi = self.image.horz_dpi - width_in_inches = px_width / horz_dpi - return Inches(width_in_inches) - - @property - def default_cy(self): - """ - Native height of this image, calculated from its height in pixels and - vertical dots per inch (dpi). - """ - px_height = self.image.px_height - horz_dpi = self.image.horz_dpi - height_in_emu = 914400 * px_height / horz_dpi - return Emu(height_in_emu) - - @property - def filename(self): - """ - Filename from which this image part was originally created. A generic - name, e.g. 'image.png', is substituted if no name is available, for - example when the image was loaded from an unnamed stream. In that - case a default extension is applied based on the detected MIME type - of the image. - """ - if self._image is not None: - return self._image.filename - return 'image.%s' % self.partname.ext - - @classmethod - def from_image(cls, image, partname): - """ - Return an |ImagePart| instance newly created from *image* and - assigned *partname*. - """ - return ImagePart(partname, image.content_type, image.blob, image) - - @property - def image(self): - if self._image is None: - self._image = Image.from_blob(self.blob) - return self._image - - @classmethod - def load(cls, partname, content_type, blob, package): - """ - Called by ``docx.opc.package.PartFactory`` to load an image part from - a package being opened by ``Document(...)`` call. - """ - return cls(partname, content_type, blob) - - @property - def sha1(self): - """ - SHA1 hash digest of the blob of this image part. - """ - return hashlib.sha1(self._blob).hexdigest() diff --git a/build/lib/docx/parts/numbering.py b/build/lib/docx/parts/numbering.py deleted file mode 100644 index e324c5aac..000000000 --- a/build/lib/docx/parts/numbering.py +++ /dev/null @@ -1,47 +0,0 @@ -# encoding: utf-8 - -""" -|NumberingPart| and closely related objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..opc.part import XmlPart -from ..shared import lazyproperty - - -class NumberingPart(XmlPart): - """ - Proxy for the numbering.xml part containing numbering definitions for - a document or glossary. - """ - @classmethod - def new(cls): - """ - Return newly created empty numbering part, containing only the root - ```` element. - """ - raise NotImplementedError - - @lazyproperty - def numbering_definitions(self): - """ - The |_NumberingDefinitions| instance containing the numbering - definitions ( element proxies) for this numbering part. - """ - return _NumberingDefinitions(self._element) - - -class _NumberingDefinitions(object): - """ - Collection of |_NumberingDefinition| instances corresponding to the - ```` elements in a numbering part. - """ - def __init__(self, numbering_elm): - super(_NumberingDefinitions, self).__init__() - self._numbering = numbering_elm - - def __len__(self): - return len(self._numbering.num_lst) diff --git a/build/lib/docx/parts/settings.py b/build/lib/docx/parts/settings.py deleted file mode 100644 index a701b1726..000000000 --- a/build/lib/docx/parts/settings.py +++ /dev/null @@ -1,54 +0,0 @@ -# encoding: utf-8 - -""" -|SettingsPart| and closely related objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -import os - -from ..opc.constants import CONTENT_TYPE as CT -from ..opc.packuri import PackURI -from ..opc.part import XmlPart -from ..oxml import parse_xml -from ..settings import Settings - - -class SettingsPart(XmlPart): - """ - Document-level settings part of a WordprocessingML (WML) package. - """ - @classmethod - def default(cls, package): - """ - Return a newly created settings part, containing a default - `w:settings` element tree. - """ - partname = PackURI('/word/settings.xml') - content_type = CT.WML_SETTINGS - element = parse_xml(cls._default_settings_xml()) - return cls(partname, content_type, element, package) - - @property - def settings(self): - """ - A |Settings| proxy object for the `w:settings` element in this part, - containing the document-level settings for this document. - """ - return Settings(self.element) - - @classmethod - def _default_settings_xml(cls): - """ - Return a bytestream containing XML for a default settings part. - """ - path = os.path.join( - os.path.split(__file__)[0], '..', 'templates', - 'default-settings.xml' - ) - with open(path, 'rb') as f: - xml_bytes = f.read() - return xml_bytes diff --git a/build/lib/docx/parts/styles.py b/build/lib/docx/parts/styles.py deleted file mode 100644 index 00c7cb3c3..000000000 --- a/build/lib/docx/parts/styles.py +++ /dev/null @@ -1,55 +0,0 @@ -# encoding: utf-8 - -""" -Provides StylesPart and related objects -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -import os - -from ..opc.constants import CONTENT_TYPE as CT -from ..opc.packuri import PackURI -from ..opc.part import XmlPart -from ..oxml import parse_xml -from ..styles.styles import Styles - - -class StylesPart(XmlPart): - """ - Proxy for the styles.xml part containing style definitions for a document - or glossary. - """ - @classmethod - def default(cls, package): - """ - Return a newly created styles part, containing a default set of - elements. - """ - partname = PackURI('/word/styles.xml') - content_type = CT.WML_STYLES - element = parse_xml(cls._default_styles_xml()) - return cls(partname, content_type, element, package) - - @property - def styles(self): - """ - The |_Styles| instance containing the styles ( element - proxies) for this styles part. - """ - return Styles(self.element) - - @classmethod - def _default_styles_xml(cls): - """ - Return a bytestream containing XML for a default styles part. - """ - path = os.path.join( - os.path.split(__file__)[0], '..', 'templates', - 'default-styles.xml' - ) - with open(path, 'rb') as f: - xml_bytes = f.read() - return xml_bytes diff --git a/build/lib/docx/section.py b/build/lib/docx/section.py deleted file mode 100644 index 16221243b..000000000 --- a/build/lib/docx/section.py +++ /dev/null @@ -1,185 +0,0 @@ -# encoding: utf-8 - -""" -The |Section| object and related proxy classes. -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from collections import Sequence - - -class Sections(Sequence): - """ - Sequence of |Section| objects corresponding to the sections in the - document. Supports ``len()``, iteration, and indexed access. - """ - def __init__(self, document_elm): - super(Sections, self).__init__() - self._document_elm = document_elm - - def __getitem__(self, key): - if isinstance(key, slice): - sectPr_lst = self._document_elm.sectPr_lst[key] - return [Section(sectPr) for sectPr in sectPr_lst] - sectPr = self._document_elm.sectPr_lst[key] - return Section(sectPr) - - def __iter__(self): - for sectPr in self._document_elm.sectPr_lst: - yield Section(sectPr) - - def __len__(self): - return len(self._document_elm.sectPr_lst) - - -class Section(object): - """ - Document section, providing access to section and page setup settings. - """ - def __init__(self, sectPr): - super(Section, self).__init__() - self._sectPr = sectPr - - @property - def bottom_margin(self): - """ - |Length| object representing the bottom margin for all pages in this - section in English Metric Units. - """ - return self._sectPr.bottom_margin - - @bottom_margin.setter - def bottom_margin(self, value): - self._sectPr.bottom_margin = value - - @property - def footer_distance(self): - """ - |Length| object representing the distance from the bottom edge of the - page to the bottom edge of the footer. |None| if no setting is present - in the XML. - """ - return self._sectPr.footer - - @footer_distance.setter - def footer_distance(self, value): - self._sectPr.footer = value - - @property - def gutter(self): - """ - |Length| object representing the page gutter size in English Metric - Units for all pages in this section. The page gutter is extra spacing - added to the *inner* margin to ensure even margins after page - binding. - """ - return self._sectPr.gutter - - @gutter.setter - def gutter(self, value): - self._sectPr.gutter = value - - @property - def header_distance(self): - """ - |Length| object representing the distance from the top edge of the - page to the top edge of the header. |None| if no setting is present - in the XML. - """ - return self._sectPr.header - - @header_distance.setter - def header_distance(self, value): - self._sectPr.header = value - - @property - def left_margin(self): - """ - |Length| object representing the left margin for all pages in this - section in English Metric Units. - """ - return self._sectPr.left_margin - - @left_margin.setter - def left_margin(self, value): - self._sectPr.left_margin = value - - @property - def orientation(self): - """ - Member of the :ref:`WdOrientation` enumeration specifying the page - orientation for this section, one of ``WD_ORIENT.PORTRAIT`` or - ``WD_ORIENT.LANDSCAPE``. - """ - return self._sectPr.orientation - - @orientation.setter - def orientation(self, value): - self._sectPr.orientation = value - - @property - def page_height(self): - """ - Total page height used for this section, inclusive of all edge spacing - values such as margins. Page orientation is taken into account, so - for example, its expected value would be ``Inches(8.5)`` for - letter-sized paper when orientation is landscape. - """ - return self._sectPr.page_height - - @page_height.setter - def page_height(self, value): - self._sectPr.page_height = value - - @property - def page_width(self): - """ - Total page width used for this section, inclusive of all edge spacing - values such as margins. Page orientation is taken into account, so - for example, its expected value would be ``Inches(11)`` for - letter-sized paper when orientation is landscape. - """ - return self._sectPr.page_width - - @page_width.setter - def page_width(self, value): - self._sectPr.page_width = value - - @property - def right_margin(self): - """ - |Length| object representing the right margin for all pages in this - section in English Metric Units. - """ - return self._sectPr.right_margin - - @right_margin.setter - def right_margin(self, value): - self._sectPr.right_margin = value - - @property - def start_type(self): - """ - The member of the :ref:`WdSectionStart` enumeration corresponding to - the initial break behavior of this section, e.g. - ``WD_SECTION.ODD_PAGE`` if the section should begin on the next odd - page. - """ - return self._sectPr.start_type - - @start_type.setter - def start_type(self, value): - self._sectPr.start_type = value - - @property - def top_margin(self): - """ - |Length| object representing the top margin for all pages in this - section in English Metric Units. - """ - return self._sectPr.top_margin - - @top_margin.setter - def top_margin(self, value): - self._sectPr.top_margin = value diff --git a/build/lib/docx/settings.py b/build/lib/docx/settings.py deleted file mode 100644 index 737146697..000000000 --- a/build/lib/docx/settings.py +++ /dev/null @@ -1,20 +0,0 @@ -# encoding: utf-8 - -""" -Settings object, providing access to document-level settings. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from .shared import ElementProxy - - -class Settings(ElementProxy): - """ - Provides access to document-level settings for a document. Accessed using - the :attr:`.Document.settings` property. - """ - - __slots__ = () diff --git a/build/lib/docx/shape.py b/build/lib/docx/shape.py deleted file mode 100644 index e4f885d73..000000000 --- a/build/lib/docx/shape.py +++ /dev/null @@ -1,103 +0,0 @@ -# encoding: utf-8 - -""" -Objects related to shapes, visual objects that appear on the drawing layer of -a document. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from .enum.shape import WD_INLINE_SHAPE -from .oxml.ns import nsmap -from .shared import Parented - - -class InlineShapes(Parented): - """ - Sequence of |InlineShape| instances, supporting len(), iteration, and - indexed access. - """ - def __init__(self, body_elm, parent): - super(InlineShapes, self).__init__(parent) - self._body = body_elm - - def __getitem__(self, idx): - """ - Provide indexed access, e.g. 'inline_shapes[idx]' - """ - try: - inline = self._inline_lst[idx] - except IndexError: - msg = "inline shape index [%d] out of range" % idx - raise IndexError(msg) - return InlineShape(inline) - - def __iter__(self): - return (InlineShape(inline) for inline in self._inline_lst) - - def __len__(self): - return len(self._inline_lst) - - @property - def _inline_lst(self): - body = self._body - xpath = '//w:p/w:r/w:drawing/wp:inline' - return body.xpath(xpath) - - -class InlineShape(object): - """ - Proxy for an ```` element, representing the container for an - inline graphical object. - """ - def __init__(self, inline): - super(InlineShape, self).__init__() - self._inline = inline - - @property - def height(self): - """ - Read/write. The display height of this inline shape as an |Emu| - instance. - """ - return self._inline.extent.cy - - @height.setter - def height(self, cy): - self._inline.extent.cy = cy - self._inline.graphic.graphicData.pic.spPr.cy = cy - - @property - def type(self): - """ - The type of this inline shape as a member of - ``docx.enum.shape.WD_INLINE_SHAPE``, e.g. ``LINKED_PICTURE``. - Read-only. - """ - graphicData = self._inline.graphic.graphicData - uri = graphicData.uri - if uri == nsmap['pic']: - blip = graphicData.pic.blipFill.blip - if blip.link is not None: - return WD_INLINE_SHAPE.LINKED_PICTURE - return WD_INLINE_SHAPE.PICTURE - if uri == nsmap['c']: - return WD_INLINE_SHAPE.CHART - if uri == nsmap['dgm']: - return WD_INLINE_SHAPE.SMART_ART - return WD_INLINE_SHAPE.NOT_IMPLEMENTED - - @property - def width(self): - """ - Read/write. The display width of this inline shape as an |Emu| - instance. - """ - return self._inline.extent.cx - - @width.setter - def width(self, cx): - self._inline.extent.cx = cx - self._inline.graphic.graphicData.pic.spPr.cx = cx diff --git a/build/lib/docx/shared.py b/build/lib/docx/shared.py deleted file mode 100644 index 919964325..000000000 --- a/build/lib/docx/shared.py +++ /dev/null @@ -1,250 +0,0 @@ -# encoding: utf-8 - -""" -Objects shared by docx modules. -""" - -from __future__ import absolute_import, print_function, unicode_literals - - -class Length(int): - """ - Base class for length constructor classes Inches, Cm, Mm, Px, and Emu. - Behaves as an int count of English Metric Units, 914,400 to the inch, - 36,000 to the mm. Provides convenience unit conversion methods in the form - of read-only properties. Immutable. - """ - _EMUS_PER_INCH = 914400 - _EMUS_PER_CM = 360000 - _EMUS_PER_MM = 36000 - _EMUS_PER_PT = 12700 - _EMUS_PER_TWIP = 635 - - def __new__(cls, emu): - return int.__new__(cls, emu) - - @property - def cm(self): - """ - The equivalent length expressed in centimeters (float). - """ - return self / float(self._EMUS_PER_CM) - - @property - def emu(self): - """ - The equivalent length expressed in English Metric Units (int). - """ - return self - - @property - def inches(self): - """ - The equivalent length expressed in inches (float). - """ - return self / float(self._EMUS_PER_INCH) - - @property - def mm(self): - """ - The equivalent length expressed in millimeters (float). - """ - return self / float(self._EMUS_PER_MM) - - @property - def pt(self): - """ - Floating point length in points - """ - return self / float(self._EMUS_PER_PT) - - @property - def twips(self): - """ - The equivalent length expressed in twips (int). - """ - return int(round(self / float(self._EMUS_PER_TWIP))) - - -class Inches(Length): - """ - Convenience constructor for length in inches, e.g. - ``width = Inches(0.5)``. - """ - def __new__(cls, inches): - emu = int(inches * Length._EMUS_PER_INCH) - return Length.__new__(cls, emu) - - -class Cm(Length): - """ - Convenience constructor for length in centimeters, e.g. - ``height = Cm(12)``. - """ - def __new__(cls, cm): - emu = int(cm * Length._EMUS_PER_CM) - return Length.__new__(cls, emu) - - -class Emu(Length): - """ - Convenience constructor for length in English Metric Units, e.g. - ``width = Emu(457200)``. - """ - def __new__(cls, emu): - return Length.__new__(cls, int(emu)) - - -class Mm(Length): - """ - Convenience constructor for length in millimeters, e.g. - ``width = Mm(240.5)``. - """ - def __new__(cls, mm): - emu = int(mm * Length._EMUS_PER_MM) - return Length.__new__(cls, emu) - - -class Pt(Length): - """ - Convenience value class for specifying a length in points - """ - def __new__(cls, points): - emu = int(points * Length._EMUS_PER_PT) - return Length.__new__(cls, emu) - - -class Twips(Length): - """ - Convenience constructor for length in twips, e.g. ``width = Twips(42)``. - A twip is a twentieth of a point, 635 EMU. - """ - def __new__(cls, twips): - emu = int(twips * Length._EMUS_PER_TWIP) - return Length.__new__(cls, emu) - - -class RGBColor(tuple): - """ - Immutable value object defining a particular RGB color. - """ - def __new__(cls, r, g, b): - msg = 'RGBColor() takes three integer values 0-255' - for val in (r, g, b): - if not isinstance(val, int) or val < 0 or val > 255: - raise ValueError(msg) - return super(RGBColor, cls).__new__(cls, (r, g, b)) - - def __repr__(self): - return 'RGBColor(0x%02x, 0x%02x, 0x%02x)' % self - - def __str__(self): - """ - Return a hex string rgb value, like '3C2F80' - """ - return '%02X%02X%02X' % self - - @classmethod - def from_string(cls, rgb_hex_str): - """ - Return a new instance from an RGB color hex string like ``'3C2F80'``. - """ - r = int(rgb_hex_str[:2], 16) - g = int(rgb_hex_str[2:4], 16) - b = int(rgb_hex_str[4:], 16) - return cls(r, g, b) - - -def lazyproperty(f): - """ - @lazyprop decorator. Decorated method will be called only on first access - to calculate a cached property value. After that, the cached value is - returned. - """ - cache_attr_name = '_%s' % f.__name__ # like '_foobar' for prop 'foobar' - docstring = f.__doc__ - - def get_prop_value(obj): - try: - return getattr(obj, cache_attr_name) - except AttributeError: - value = f(obj) - setattr(obj, cache_attr_name, value) - return value - - return property(get_prop_value, doc=docstring) - - -def write_only_property(f): - """ - @write_only_property decorator. Creates a property (descriptor attribute) - that accepts assignment, but not getattr (use in an expression). - """ - docstring = f.__doc__ - - return property(fset=f, doc=docstring) - - -class ElementProxy(object): - """ - Base class for lxml element proxy classes. An element proxy class is one - whose primary responsibilities are fulfilled by manipulating the - attributes and child elements of an XML element. They are the most common - type of class in python-docx other than custom element (oxml) classes. - """ - - __slots__ = ('_element', '_parent') - - def __init__(self, element, parent=None): - self._element = element - self._parent = parent - - def __eq__(self, other): - """ - Return |True| if this proxy object refers to the same oxml element as - does *other*. ElementProxy objects are value objects and should - maintain no mutable local state. Equality for proxy objects is - defined as referring to the same XML element, whether or not they are - the same proxy object instance. - """ - if not isinstance(other, ElementProxy): - return False - return self._element is other._element - - def __ne__(self, other): - if not isinstance(other, ElementProxy): - return True - return self._element is not other._element - - @property - def element(self): - """ - The lxml element proxied by this object. - """ - return self._element - - @property - def part(self): - """ - The package part containing this object - """ - return self._parent.part - - -class Parented(object): - """ - Provides common services for document elements that occur below a part - but may occasionally require an ancestor object to provide a service, - such as add or drop a relationship. Provides ``self._parent`` attribute - to subclasses. - """ - def __init__(self, parent): - super(Parented, self).__init__() - self._parent = parent - - @property - def part(self): - """ - The package part containing this object - """ - return self._parent.part diff --git a/build/lib/docx/styles/__init__.py b/build/lib/docx/styles/__init__.py deleted file mode 100644 index 3eff43e55..000000000 --- a/build/lib/docx/styles/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# encoding: utf-8 - -""" -Sub-package module for docx.styles sub-package. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - - -class BabelFish(object): - """ - Translates special-case style names from UI name (e.g. Heading 1) to - internal/styles.xml name (e.g. heading 1) and back. - """ - - style_aliases = ( - ('Caption', 'caption'), - ('Heading 1', 'heading 1'), - ('Heading 2', 'heading 2'), - ('Heading 3', 'heading 3'), - ('Heading 4', 'heading 4'), - ('Heading 5', 'heading 5'), - ('Heading 6', 'heading 6'), - ('Heading 7', 'heading 7'), - ('Heading 8', 'heading 8'), - ('Heading 9', 'heading 9'), - ) - - internal_style_names = dict(style_aliases) - ui_style_names = dict((item[1], item[0]) for item in style_aliases) - - @classmethod - def ui2internal(cls, ui_style_name): - """ - Return the internal style name corresponding to *ui_style_name*, such - as 'heading 1' for 'Heading 1'. - """ - return cls.internal_style_names.get(ui_style_name, ui_style_name) - - @classmethod - def internal2ui(cls, internal_style_name): - """ - Return the user interface style name corresponding to - *internal_style_name*, such as 'Heading 1' for 'heading 1'. - """ - return cls.ui_style_names.get( - internal_style_name, internal_style_name - ) diff --git a/build/lib/docx/styles/latent.py b/build/lib/docx/styles/latent.py deleted file mode 100644 index 99b1514ff..000000000 --- a/build/lib/docx/styles/latent.py +++ /dev/null @@ -1,224 +0,0 @@ -# encoding: utf-8 - -""" -Latent style-related objects. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from . import BabelFish -from ..shared import ElementProxy - - -class LatentStyles(ElementProxy): - """ - Provides access to the default behaviors for latent styles in this - document and to the collection of |_LatentStyle| objects that define - overrides of those defaults for a particular named latent style. - """ - - __slots__ = () - - def __getitem__(self, key): - """ - Enables dictionary-style access to a latent style by name. - """ - style_name = BabelFish.ui2internal(key) - lsdException = self._element.get_by_name(style_name) - if lsdException is None: - raise KeyError("no latent style with name '%s'" % key) - return _LatentStyle(lsdException) - - def __iter__(self): - return (_LatentStyle(ls) for ls in self._element.lsdException_lst) - - def __len__(self): - return len(self._element.lsdException_lst) - - def add_latent_style(self, name): - """ - Return a newly added |_LatentStyle| object to override the inherited - defaults defined in this latent styles object for the built-in style - having *name*. - """ - lsdException = self._element.add_lsdException() - lsdException.name = BabelFish.ui2internal(name) - return _LatentStyle(lsdException) - - @property - def default_priority(self): - """ - Integer between 0 and 99 inclusive specifying the default sort order - for latent styles in style lists and the style gallery. |None| if no - value is assigned, which causes Word to use the default value 99. - """ - return self._element.defUIPriority - - @default_priority.setter - def default_priority(self, value): - self._element.defUIPriority = value - - @property - def default_to_hidden(self): - """ - Boolean specifying whether the default behavior for latent styles is - to be hidden. A hidden style does not appear in the recommended list - or in the style gallery. - """ - return self._element.bool_prop('defSemiHidden') - - @default_to_hidden.setter - def default_to_hidden(self, value): - self._element.set_bool_prop('defSemiHidden', value) - - @property - def default_to_locked(self): - """ - Boolean specifying whether the default behavior for latent styles is - to be locked. A locked style does not appear in the styles panel or - the style gallery and cannot be applied to document content. This - behavior is only active when formatting protection is turned on for - the document (via the Developer menu). - """ - return self._element.bool_prop('defLockedState') - - @default_to_locked.setter - def default_to_locked(self, value): - self._element.set_bool_prop('defLockedState', value) - - @property - def default_to_quick_style(self): - """ - Boolean specifying whether the default behavior for latent styles is - to appear in the style gallery when not hidden. - """ - return self._element.bool_prop('defQFormat') - - @default_to_quick_style.setter - def default_to_quick_style(self, value): - self._element.set_bool_prop('defQFormat', value) - - @property - def default_to_unhide_when_used(self): - """ - Boolean specifying whether the default behavior for latent styles is - to be unhidden when first applied to content. - """ - return self._element.bool_prop('defUnhideWhenUsed') - - @default_to_unhide_when_used.setter - def default_to_unhide_when_used(self, value): - self._element.set_bool_prop('defUnhideWhenUsed', value) - - @property - def load_count(self): - """ - Integer specifying the number of built-in styles to initialize to the - defaults specified in this |LatentStyles| object. |None| if there is - no setting in the XML (very uncommon). The default Word 2011 template - sets this value to 276, accounting for the built-in styles in Word - 2010. - """ - return self._element.count - - @load_count.setter - def load_count(self, value): - self._element.count = value - - -class _LatentStyle(ElementProxy): - """ - Proxy for an `w:lsdException` element, which specifies display behaviors - for a built-in style when no definition for that style is stored yet in - the `styles.xml` part. The values in this element override the defaults - specified in the parent `w:latentStyles` element. - """ - - __slots__ = () - - def delete(self): - """ - Remove this latent style definition such that the defaults defined in - the containing |LatentStyles| object provide the effective value for - each of its attributes. Attempting to access any attributes on this - object after calling this method will raise |AttributeError|. - """ - self._element.delete() - self._element = None - - @property - def hidden(self): - """ - Tri-state value specifying whether this latent style should appear in - the recommended list. |None| indicates the effective value is - inherited from the parent ```` element. - """ - return self._element.on_off_prop('semiHidden') - - @hidden.setter - def hidden(self, value): - self._element.set_on_off_prop('semiHidden', value) - - @property - def locked(self): - """ - Tri-state value specifying whether this latent styles is locked. - A locked style does not appear in the styles panel or the style - gallery and cannot be applied to document content. This behavior is - only active when formatting protection is turned on for the document - (via the Developer menu). - """ - return self._element.on_off_prop('locked') - - @locked.setter - def locked(self, value): - self._element.set_on_off_prop('locked', value) - - @property - def name(self): - """ - The name of the built-in style this exception applies to. - """ - return BabelFish.internal2ui(self._element.name) - - @property - def priority(self): - """ - The integer sort key for this latent style in the Word UI. - """ - return self._element.uiPriority - - @priority.setter - def priority(self, value): - self._element.uiPriority = value - - @property - def quick_style(self): - """ - Tri-state value specifying whether this latent style should appear in - the Word styles gallery when not hidden. |None| indicates the - effective value should be inherited from the default values in its - parent |LatentStyles| object. - """ - return self._element.on_off_prop('qFormat') - - @quick_style.setter - def quick_style(self, value): - self._element.set_on_off_prop('qFormat', value) - - @property - def unhide_when_used(self): - """ - Tri-state value specifying whether this style should have its - :attr:`hidden` attribute set |False| the next time the style is - applied to content. |None| indicates the effective value should be - inherited from the default specified by its parent |LatentStyles| - object. - """ - return self._element.on_off_prop('unhideWhenUsed') - - @unhide_when_used.setter - def unhide_when_used(self, value): - self._element.set_on_off_prop('unhideWhenUsed', value) diff --git a/build/lib/docx/styles/style.py b/build/lib/docx/styles/style.py deleted file mode 100644 index 24371b231..000000000 --- a/build/lib/docx/styles/style.py +++ /dev/null @@ -1,265 +0,0 @@ -# encoding: utf-8 - -""" -Style object hierarchy. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from . import BabelFish -from ..enum.style import WD_STYLE_TYPE -from ..shared import ElementProxy -from ..text.font import Font -from ..text.parfmt import ParagraphFormat - - -def StyleFactory(style_elm): - """ - Return a style object of the appropriate |BaseStyle| subclass, according - to the type of *style_elm*. - """ - style_cls = { - WD_STYLE_TYPE.PARAGRAPH: _ParagraphStyle, - WD_STYLE_TYPE.CHARACTER: _CharacterStyle, - WD_STYLE_TYPE.TABLE: _TableStyle, - WD_STYLE_TYPE.LIST: _NumberingStyle - }[style_elm.type] - - return style_cls(style_elm) - - -class BaseStyle(ElementProxy): - """ - Base class for the various types of style object, paragraph, character, - table, and numbering. These properties and methods are inherited by all - style objects. - """ - - __slots__ = () - - @property - def builtin(self): - """ - Read-only. |True| if this style is a built-in style. |False| - indicates it is a custom (user-defined) style. Note this value is - based on the presence of a `customStyle` attribute in the XML, not on - specific knowledge of which styles are built into Word. - """ - return not self._element.customStyle - - def delete(self): - """ - Remove this style definition from the document. Note that calling - this method does not remove or change the style applied to any - document content. Content items having the deleted style will be - rendered using the default style, as is any content with a style not - defined in the document. - """ - self._element.delete() - self._element = None - - @property - def hidden(self): - """ - |True| if display of this style in the style gallery and list of - recommended styles is suppressed. |False| otherwise. In order to be - shown in the style gallery, this value must be |False| and - :attr:`.quick_style` must be |True|. - """ - return self._element.semiHidden_val - - @hidden.setter - def hidden(self, value): - self._element.semiHidden_val = value - - @property - def locked(self): - """ - Read/write Boolean. |True| if this style is locked. A locked style - does not appear in the styles panel or the style gallery and cannot - be applied to document content. This behavior is only active when - formatting protection is turned on for the document (via the - Developer menu). - """ - return self._element.locked_val - - @locked.setter - def locked(self, value): - self._element.locked_val = value - - @property - def name(self): - """ - The UI name of this style. - """ - name = self._element.name_val - if name is None: - return None - return BabelFish.internal2ui(name) - - @name.setter - def name(self, value): - self._element.name_val = value - - @property - def priority(self): - """ - The integer sort key governing display sequence of this style in the - Word UI. |None| indicates no setting is defined, causing Word to use - the default value of 0. Style name is used as a secondary sort key to - resolve ordering of styles having the same priority value. - """ - return self._element.uiPriority_val - - @priority.setter - def priority(self, value): - self._element.uiPriority_val = value - - @property - def quick_style(self): - """ - |True| if this style should be displayed in the style gallery when - :attr:`.hidden` is |False|. Read/write Boolean. - """ - return self._element.qFormat_val - - @quick_style.setter - def quick_style(self, value): - self._element.qFormat_val = value - - @property - def style_id(self): - """ - The unique key name (string) for this style. This value is subject to - rewriting by Word and should generally not be changed unless you are - familiar with the internals involved. - """ - return self._element.styleId - - @style_id.setter - def style_id(self, value): - self._element.styleId = value - - @property - def type(self): - """ - Member of :ref:`WdStyleType` corresponding to the type of this style, - e.g. ``WD_STYLE_TYPE.PARAGRAPH``. - """ - type = self._element.type - if type is None: - return WD_STYLE_TYPE.PARAGRAPH - return type - - @property - def unhide_when_used(self): - """ - |True| if an application should make this style visible the next time - it is applied to content. False otherwise. Note that |docx| does not - automatically unhide a style having |True| for this attribute when it - is applied to content. - """ - return self._element.unhideWhenUsed_val - - @unhide_when_used.setter - def unhide_when_used(self, value): - self._element.unhideWhenUsed_val = value - - -class _CharacterStyle(BaseStyle): - """ - A character style. A character style is applied to a |Run| object and - primarily provides character-level formatting via the |Font| object in - its :attr:`.font` property. - """ - - __slots__ = () - - @property - def base_style(self): - """ - Style object this style inherits from or |None| if this style is - not based on another style. - """ - base_style = self._element.base_style - if base_style is None: - return None - return StyleFactory(base_style) - - @base_style.setter - def base_style(self, style): - style_id = style.style_id if style is not None else None - self._element.basedOn_val = style_id - - @property - def font(self): - """ - The |Font| object providing access to the character formatting - properties for this style, such as font name and size. - """ - return Font(self._element) - - -class _ParagraphStyle(_CharacterStyle): - """ - A paragraph style. A paragraph style provides both character formatting - and paragraph formatting such as indentation and line-spacing. - """ - - __slots__ = () - - def __repr__(self): - return '_ParagraphStyle(\'%s\') id: %s' % (self.name, id(self)) - - @property - def next_paragraph_style(self): - """ - |_ParagraphStyle| object representing the style to be applied - automatically to a new paragraph inserted after a paragraph of this - style. Returns self if no next paragraph style is defined. Assigning - |None| or *self* removes the setting such that new paragraphs are - created using this same style. - """ - next_style_elm = self._element.next_style - if next_style_elm is None: - return self - if next_style_elm.type != WD_STYLE_TYPE.PARAGRAPH: - return self - return StyleFactory(next_style_elm) - - @next_paragraph_style.setter - def next_paragraph_style(self, style): - if style is None or style.style_id == self.style_id: - self._element._remove_next() - else: - self._element.get_or_add_next().val = style.style_id - - @property - def paragraph_format(self): - """ - The |ParagraphFormat| object providing access to the paragraph - formatting properties for this style such as indentation. - """ - return ParagraphFormat(self._element) - - -class _TableStyle(_ParagraphStyle): - """ - A table style. A table style provides character and paragraph formatting - for its contents as well as special table formatting properties. - """ - - __slots__ = () - - def __repr__(self): - return '_TableStyle(\'%s\') id: %s' % (self.name, id(self)) - - -class _NumberingStyle(BaseStyle): - """ - A numbering style. Not yet implemented. - """ - - __slots__ = () diff --git a/build/lib/docx/styles/styles.py b/build/lib/docx/styles/styles.py deleted file mode 100644 index ad27fa764..000000000 --- a/build/lib/docx/styles/styles.py +++ /dev/null @@ -1,157 +0,0 @@ -# encoding: utf-8 - -""" -Styles object, container for all objects in the styles part. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from warnings import warn - -from . import BabelFish -from .latent import LatentStyles -from ..shared import ElementProxy -from .style import BaseStyle, StyleFactory - - -class Styles(ElementProxy): - """ - A collection providing access to the styles defined in a document. - Accessed using the :attr:`.Document.styles` property. Supports ``len()``, - iteration, and dictionary-style access by style name. - """ - - __slots__ = () - - def __contains__(self, name): - """ - Enables `in` operator on style name. - """ - internal_name = BabelFish.ui2internal(name) - for style in self._element.style_lst: - if style.name_val == internal_name: - return True - return False - - def __getitem__(self, key): - """ - Enables dictionary-style access by UI name. Lookup by style id is - deprecated, triggers a warning, and will be removed in a near-future - release. - """ - style_elm = self._element.get_by_name(BabelFish.ui2internal(key)) - if style_elm is not None: - return StyleFactory(style_elm) - - style_elm = self._element.get_by_id(key) - if style_elm is not None: - msg = ( - 'style lookup by style_id is deprecated. Use style name as ' - 'key instead.' - ) - warn(msg, UserWarning, stacklevel=2) - return StyleFactory(style_elm) - - raise KeyError("no style with name '%s'" % key) - - def __iter__(self): - return (StyleFactory(style) for style in self._element.style_lst) - - def __len__(self): - return len(self._element.style_lst) - - def add_style(self, name, style_type, builtin=False): - """ - Return a newly added style object of *style_type* and identified - by *name*. A builtin style can be defined by passing True for the - optional *builtin* argument. - """ - style_name = BabelFish.ui2internal(name) - if style_name in self: - raise ValueError("document already contains style '%s'" % name) - style = self._element.add_style_of_type( - style_name, style_type, builtin - ) - return StyleFactory(style) - - def default(self, style_type): - """ - Return the default style for *style_type* or |None| if no default is - defined for that type (not common). - """ - style = self._element.default_for(style_type) - if style is None: - return None - return StyleFactory(style) - - def get_by_id(self, style_id, style_type): - """ - Return the style of *style_type* matching *style_id*. Returns the - default for *style_type* if *style_id* is not found or is |None|, or - if the style having *style_id* is not of *style_type*. - """ - if style_id is None: - return self.default(style_type) - return self._get_by_id(style_id, style_type) - - def get_style_id(self, style_or_name, style_type): - """ - Return the id of the style corresponding to *style_or_name*, or - |None| if *style_or_name* is |None|. If *style_or_name* is not - a style object, the style is looked up using *style_or_name* as - a style name, raising |ValueError| if no style with that name is - defined. Raises |ValueError| if the target style is not of - *style_type*. - """ - if style_or_name is None: - return None - elif isinstance(style_or_name, BaseStyle): - return self._get_style_id_from_style(style_or_name, style_type) - else: - return self._get_style_id_from_name(style_or_name, style_type) - - @property - def latent_styles(self): - """ - A |LatentStyles| object providing access to the default behaviors for - latent styles and the collection of |_LatentStyle| objects that - define overrides of those defaults for a particular named latent - style. - """ - return LatentStyles(self._element.get_or_add_latentStyles()) - - def _get_by_id(self, style_id, style_type): - """ - Return the style of *style_type* matching *style_id*. Returns the - default for *style_type* if *style_id* is not found or if the style - having *style_id* is not of *style_type*. - """ - style = self._element.get_by_id(style_id) - if style is None or style.type != style_type: - return self.default(style_type) - return StyleFactory(style) - - def _get_style_id_from_name(self, style_name, style_type): - """ - Return the id of the style of *style_type* corresponding to - *style_name*. Returns |None| if that style is the default style for - *style_type*. Raises |ValueError| if the named style is not found in - the document or does not match *style_type*. - """ - return self._get_style_id_from_style(self[style_name], style_type) - - def _get_style_id_from_style(self, style, style_type): - """ - Return the id of *style*, or |None| if it is the default style of - *style_type*. Raises |ValueError| if style is not of *style_type*. - """ - if style.type != style_type: - raise ValueError( - "assigned style is type %s, need type %s" % - (style.type, style_type) - ) - if style == self.default(style_type): - return None - return style.style_id diff --git a/build/lib/docx/table.py b/build/lib/docx/table.py deleted file mode 100644 index b3bc090fb..000000000 --- a/build/lib/docx/table.py +++ /dev/null @@ -1,469 +0,0 @@ -# encoding: utf-8 - -""" -The |Table| object and related proxy classes. -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from .blkcntnr import BlockItemContainer -from .enum.style import WD_STYLE_TYPE -from .oxml.simpletypes import ST_Merge -from .shared import Inches, lazyproperty, Parented - - -class Table(Parented): - """ - Proxy class for a WordprocessingML ```` element. - """ - def __init__(self, tbl, parent): - super(Table, self).__init__(parent) - self._element = self._tbl = tbl - - def add_column(self, width): - """ - Return a |_Column| object of *width*, newly added rightmost to the - table. - """ - tblGrid = self._tbl.tblGrid - gridCol = tblGrid.add_gridCol() - gridCol.w = width - for tr in self._tbl.tr_lst: - tc = tr.add_tc() - tc.width = width - return _Column(gridCol, self) - - def add_row(self): - """ - Return a |_Row| instance, newly added bottom-most to the table. - """ - tbl = self._tbl - tr = tbl.add_tr() - for gridCol in tbl.tblGrid.gridCol_lst: - tc = tr.add_tc() - tc.width = gridCol.w - return _Row(tr, self) - - @property - def alignment(self): - """ - Read/write. A member of :ref:`WdRowAlignment` or None, specifying the - positioning of this table between the page margins. |None| if no - setting is specified, causing the effective value to be inherited - from the style hierarchy. - """ - return self._tblPr.alignment - - @alignment.setter - def alignment(self, value): - self._tblPr.alignment = value - - @property - def autofit(self): - """ - |True| if column widths can be automatically adjusted to improve the - fit of cell contents. |False| if table layout is fixed. Column widths - are adjusted in either case if total column width exceeds page width. - Read/write boolean. - """ - return self._tblPr.autofit - - @autofit.setter - def autofit(self, value): - self._tblPr.autofit = value - - def cell(self, row_idx, col_idx): - """ - Return |_Cell| instance correponding to table cell at *row_idx*, - *col_idx* intersection, where (0, 0) is the top, left-most cell. - """ - cell_idx = col_idx + (row_idx * self._column_count) - return self._cells[cell_idx] - - def column_cells(self, column_idx): - """ - Sequence of cells in the column at *column_idx* in this table. - """ - cells = self._cells - idxs = range(column_idx, len(cells), self._column_count) - return [cells[idx] for idx in idxs] - - @lazyproperty - def columns(self): - """ - |_Columns| instance representing the sequence of columns in this - table. - """ - return _Columns(self._tbl, self) - - def row_cells(self, row_idx): - """ - Sequence of cells in the row at *row_idx* in this table. - """ - column_count = self._column_count - start = row_idx * column_count - end = start + column_count - return self._cells[start:end] - - @lazyproperty - def rows(self): - """ - |_Rows| instance containing the sequence of rows in this table. - """ - return _Rows(self._tbl, self) - - @property - def style(self): - """ - Read/write. A |_TableStyle| object representing the style applied to - this table. The default table style for the document (often `Normal - Table`) is returned if the table has no directly-applied style. - Assigning |None| to this property removes any directly-applied table - style causing it to inherit the default table style of the document. - Note that the style name of a table style differs slightly from that - displayed in the user interface; a hyphen, if it appears, must be - removed. For example, `Light Shading - Accent 1` becomes `Light - Shading Accent 1`. - """ - style_id = self._tbl.tblStyle_val - return self.part.get_style(style_id, WD_STYLE_TYPE.TABLE) - - @style.setter - def style(self, style_or_name): - style_id = self.part.get_style_id( - style_or_name, WD_STYLE_TYPE.TABLE - ) - self._tbl.tblStyle_val = style_id - - @property - def table(self): - """ - Provide child objects with reference to the |Table| object they - belong to, without them having to know their direct parent is - a |Table| object. This is the terminus of a series of `parent._table` - calls from an arbitrary child through its ancestors. - """ - return self - - @property - def table_direction(self): - """ - A member of :ref:`WdTableDirection` indicating the direction in which - the table cells are ordered, e.g. `WD_TABLE_DIRECTION.LTR`. |None| - indicates the value is inherited from the style hierarchy. - """ - return self._element.bidiVisual_val - - @table_direction.setter - def table_direction(self, value): - self._element.bidiVisual_val = value - - @property - def _cells(self): - """ - A sequence of |_Cell| objects, one for each cell of the layout grid. - If the table contains a span, one or more |_Cell| object references - are repeated. - """ - col_count = self._column_count - cells = [] - for tc in self._tbl.iter_tcs(): - for grid_span_idx in range(tc.grid_span): - if tc.vMerge == ST_Merge.CONTINUE: - cells.append(cells[-col_count]) - elif grid_span_idx > 0: - cells.append(cells[-1]) - else: - cells.append(_Cell(tc, self)) - return cells - - @property - def _column_count(self): - """ - The number of grid columns in this table. - """ - return self._tbl.col_count - - @property - def _tblPr(self): - return self._tbl.tblPr - - -class _Cell(BlockItemContainer): - """Table cell""" - - def __init__(self, tc, parent): - super(_Cell, self).__init__(tc, parent) - self._tc = self._element = tc - - def add_paragraph(self, text='', style=None): - """ - Return a paragraph newly added to the end of the content in this - cell. If present, *text* is added to the paragraph in a single run. - If specified, the paragraph style *style* is applied. If *style* is - not specified or is |None|, the result is as though the 'Normal' - style was applied. Note that the formatting of text in a cell can be - influenced by the table style. *text* can contain tab (``\\t``) - characters, which are converted to the appropriate XML form for - a tab. *text* can also include newline (``\\n``) or carriage return - (``\\r``) characters, each of which is converted to a line break. - """ - return super(_Cell, self).add_paragraph(text, style) - - def add_table(self, rows, cols): - """ - Return a table newly added to this cell after any existing cell - content, having *rows* rows and *cols* columns. An empty paragraph is - added after the table because Word requires a paragraph element as - the last element in every cell. - """ - width = self.width if self.width is not None else Inches(1) - table = super(_Cell, self).add_table(rows, cols, width) - self.add_paragraph() - return table - - def merge(self, other_cell): - """ - Return a merged cell created by spanning the rectangular region - having this cell and *other_cell* as diagonal corners. Raises - |InvalidSpanError| if the cells do not define a rectangular region. - """ - tc, tc_2 = self._tc, other_cell._tc - merged_tc = tc.merge(tc_2) - return _Cell(merged_tc, self._parent) - - @property - def paragraphs(self): - """ - List of paragraphs in the cell. A table cell is required to contain - at least one block-level element and end with a paragraph. By - default, a new cell contains a single paragraph. Read-only - """ - return super(_Cell, self).paragraphs - - @property - def tables(self): - """ - List of tables in the cell, in the order they appear. Read-only. - """ - return super(_Cell, self).tables - - @property - def text(self): - """ - The entire contents of this cell as a string of text. Assigning - a string to this property replaces all existing content with a single - paragraph containing the assigned text in a single run. - """ - return '\n'.join(p.text for p in self.paragraphs) - - @text.setter - def text(self, text): - """ - Write-only. Set entire contents of cell to the string *text*. Any - existing content or revisions are replaced. - """ - tc = self._tc - tc.clear_content() - p = tc.add_p() - r = p.add_r() - r.text = text - - @property - def vertical_alignment(self): - """Member of :ref:`WdCellVerticalAlignment` or None. - - A value of |None| indicates vertical alignment for this cell is - inherited. Assigning |None| causes any explicitly defined vertical - alignment to be removed, restoring inheritance. - """ - tcPr = self._element.tcPr - if tcPr is None: - return None - return tcPr.vAlign_val - - @vertical_alignment.setter - def vertical_alignment(self, value): - tcPr = self._element.get_or_add_tcPr() - tcPr.vAlign_val = value - - @property - def width(self): - """ - The width of this cell in EMU, or |None| if no explicit width is set. - """ - return self._tc.width - - @width.setter - def width(self, value): - self._tc.width = value - - -class _Column(Parented): - """ - Table column - """ - def __init__(self, gridCol, parent): - super(_Column, self).__init__(parent) - self._gridCol = gridCol - - @property - def cells(self): - """ - Sequence of |_Cell| instances corresponding to cells in this column. - """ - return tuple(self.table.column_cells(self._index)) - - @property - def table(self): - """ - Reference to the |Table| object this column belongs to. - """ - return self._parent.table - - @property - def width(self): - """ - The width of this column in EMU, or |None| if no explicit width is - set. - """ - return self._gridCol.w - - @width.setter - def width(self, value): - self._gridCol.w = value - - @property - def _index(self): - """ - Index of this column in its table, starting from zero. - """ - return self._gridCol.gridCol_idx - - -class _Columns(Parented): - """ - Sequence of |_Column| instances corresponding to the columns in a table. - Supports ``len()``, iteration and indexed access. - """ - def __init__(self, tbl, parent): - super(_Columns, self).__init__(parent) - self._tbl = tbl - - def __getitem__(self, idx): - """ - Provide indexed access, e.g. 'columns[0]' - """ - try: - gridCol = self._gridCol_lst[idx] - except IndexError: - msg = "column index [%d] is out of range" % idx - raise IndexError(msg) - return _Column(gridCol, self) - - def __iter__(self): - for gridCol in self._gridCol_lst: - yield _Column(gridCol, self) - - def __len__(self): - return len(self._gridCol_lst) - - @property - def table(self): - """ - Reference to the |Table| object this column collection belongs to. - """ - return self._parent.table - - @property - def _gridCol_lst(self): - """ - Sequence containing ```` elements for this table, each - representing a table column. - """ - tblGrid = self._tbl.tblGrid - return tblGrid.gridCol_lst - - -class _Row(Parented): - """ - Table row - """ - def __init__(self, tr, parent): - super(_Row, self).__init__(parent) - self._tr = self._element = tr - - @property - def cells(self): - """ - Sequence of |_Cell| instances corresponding to cells in this row. - """ - return tuple(self.table.row_cells(self._index)) - - @property - def height(self): - """ - Return a |Length| object representing the height of this cell, or - |None| if no explicit height is set. - """ - return self._tr.trHeight_val - - @height.setter - def height(self, value): - self._tr.trHeight_val = value - - @property - def height_rule(self): - """ - Return the height rule of this cell as a member of the - :ref:`WdRowHeightRule` enumeration, or |None| if no explicit - height_rule is set. - """ - return self._tr.trHeight_hRule - - @height_rule.setter - def height_rule(self, value): - self._tr.trHeight_hRule = value - - @property - def table(self): - """ - Reference to the |Table| object this row belongs to. - """ - return self._parent.table - - @property - def _index(self): - """ - Index of this row in its table, starting from zero. - """ - return self._tr.tr_idx - - -class _Rows(Parented): - """ - Sequence of |_Row| objects corresponding to the rows in a table. - Supports ``len()``, iteration, indexed access, and slicing. - """ - def __init__(self, tbl, parent): - super(_Rows, self).__init__(parent) - self._tbl = tbl - - def __getitem__(self, idx): - """ - Provide indexed access, (e.g. 'rows[0]') - """ - return list(self)[idx] - - def __iter__(self): - return (_Row(tr, self) for tr in self._tbl.tr_lst) - - def __len__(self): - return len(self._tbl.tr_lst) - - @property - def table(self): - """ - Reference to the |Table| object this row collection belongs to. - """ - return self._parent.table diff --git a/build/lib/docx/templates/default-comments.xml b/build/lib/docx/templates/default-comments.xml deleted file mode 100644 index 4ceb12ea4..000000000 --- a/build/lib/docx/templates/default-comments.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/build/lib/docx/templates/default-footnotes.xml b/build/lib/docx/templates/default-footnotes.xml deleted file mode 100644 index 5dc12e66f..000000000 --- a/build/lib/docx/templates/default-footnotes.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/build/lib/docx/templates/default-settings.xml b/build/lib/docx/templates/default-settings.xml deleted file mode 100644 index fda1adef7..000000000 --- a/build/lib/docx/templates/default-settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/build/lib/docx/templates/default-src.docx b/build/lib/docx/templates/default-src.docx deleted file mode 100644 index 31c8e20b4a21a0c45d9b22b1c3f68808fca37279..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77650 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/lib/docx/templates/default.docx b/build/lib/docx/templates/default.docx deleted file mode 100644 index dd00cd2fc1bbea08f8a92e1ca0b1afd1e276d866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38003 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 element and providing - access to character properties such as font name, font size, bold, and - subscript. - """ - - __slots__ = () - - @property - def all_caps(self): - """ - Read/write. Causes text in this font to appear in capital letters. - """ - return self._get_bool_prop('caps') - - @all_caps.setter - def all_caps(self, value): - self._set_bool_prop('caps', value) - - @property - def bold(self): - """ - Read/write. Causes text in this font to appear in bold. - """ - return self._get_bool_prop('b') - - @bold.setter - def bold(self, value): - self._set_bool_prop('b', value) - - @property - def color(self): - """ - A |ColorFormat| object providing a way to get and set the text color - for this font. - """ - return ColorFormat(self._element) - - @property - def complex_script(self): - """ - Read/write tri-state value. When |True|, causes the characters in the - run to be treated as complex script regardless of their Unicode - values. - """ - return self._get_bool_prop('cs') - - @complex_script.setter - def complex_script(self, value): - self._set_bool_prop('cs', value) - - @property - def cs_bold(self): - """ - Read/write tri-state value. When |True|, causes the complex script - characters in the run to be displayed in bold typeface. - """ - return self._get_bool_prop('bCs') - - @cs_bold.setter - def cs_bold(self, value): - self._set_bool_prop('bCs', value) - - @property - def cs_italic(self): - """ - Read/write tri-state value. When |True|, causes the complex script - characters in the run to be displayed in italic typeface. - """ - return self._get_bool_prop('iCs') - - @cs_italic.setter - def cs_italic(self, value): - self._set_bool_prop('iCs', value) - - @property - def double_strike(self): - """ - Read/write tri-state value. When |True|, causes the text in the run - to appear with double strikethrough. - """ - return self._get_bool_prop('dstrike') - - @double_strike.setter - def double_strike(self, value): - self._set_bool_prop('dstrike', value) - - @property - def emboss(self): - """ - Read/write tri-state value. When |True|, causes the text in the run - to appear as if raised off the page in relief. - """ - return self._get_bool_prop('emboss') - - @emboss.setter - def emboss(self, value): - self._set_bool_prop('emboss', value) - - @property - def hidden(self): - """ - Read/write tri-state value. When |True|, causes the text in the run - to be hidden from display, unless applications settings force hidden - text to be shown. - """ - return self._get_bool_prop('vanish') - - @hidden.setter - def hidden(self, value): - self._set_bool_prop('vanish', value) - - @property - def highlight_color(self): - """ - A member of :ref:`WdColorIndex` indicating the color of highlighting - applied, or `None` if no highlighting is applied. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.highlight_val - - @highlight_color.setter - def highlight_color(self, value): - rPr = self._element.get_or_add_rPr() - rPr.highlight_val = value - - @property - def italic(self): - """ - Read/write tri-state value. When |True|, causes the text of the run - to appear in italics. |None| indicates the effective value is - inherited from the style hierarchy. - """ - return self._get_bool_prop('i') - - @italic.setter - def italic(self, value): - self._set_bool_prop('i', value) - - @property - def imprint(self): - """ - Read/write tri-state value. When |True|, causes the text in the run - to appear as if pressed into the page. - """ - return self._get_bool_prop('imprint') - - @imprint.setter - def imprint(self, value): - self._set_bool_prop('imprint', value) - - @property - def math(self): - """ - Read/write tri-state value. When |True|, specifies this run contains - WML that should be handled as though it was Office Open XML Math. - """ - return self._get_bool_prop('oMath') - - @math.setter - def math(self, value): - self._set_bool_prop('oMath', value) - - @property - def name(self): - """ - Get or set the typeface name for this |Font| instance, causing the - text it controls to appear in the named font, if a matching font is - found. |None| indicates the typeface is inherited from the style - hierarchy. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.rFonts_ascii - - @name.setter - def name(self, value): - rPr = self._element.get_or_add_rPr() - rPr.rFonts_ascii = value - rPr.rFonts_hAnsi = value - - @property - def no_proof(self): - """ - Read/write tri-state value. When |True|, specifies that the contents - of this run should not report any errors when the document is scanned - for spelling and grammar. - """ - return self._get_bool_prop('noProof') - - @no_proof.setter - def no_proof(self, value): - self._set_bool_prop('noProof', value) - - @property - def outline(self): - """ - Read/write tri-state value. When |True| causes the characters in the - run to appear as if they have an outline, by drawing a one pixel wide - border around the inside and outside borders of each character glyph. - """ - return self._get_bool_prop('outline') - - @outline.setter - def outline(self, value): - self._set_bool_prop('outline', value) - - @property - def rtl(self): - """ - Read/write tri-state value. When |True| causes the text in the run - to have right-to-left characteristics. - """ - return self._get_bool_prop('rtl') - - @rtl.setter - def rtl(self, value): - self._set_bool_prop('rtl', value) - - @property - def shadow(self): - """ - Read/write tri-state value. When |True| causes the text in the run - to appear as if each character has a shadow. - """ - return self._get_bool_prop('shadow') - - @shadow.setter - def shadow(self, value): - self._set_bool_prop('shadow', value) - - @property - def size(self): - """ - Read/write |Length| value or |None|, indicating the font height in - English Metric Units (EMU). |None| indicates the font size should be - inherited from the style hierarchy. |Length| is a subclass of |int| - having properties for convenient conversion into points or other - length units. The :class:`docx.shared.Pt` class allows convenient - specification of point values:: - - >> font.size = Pt(24) - >> font.size - 304800 - >> font.size.pt - 24.0 - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.sz_val - - @size.setter - def size(self, emu): - rPr = self._element.get_or_add_rPr() - rPr.sz_val = emu - - @property - def small_caps(self): - """ - Read/write tri-state value. When |True| causes the lowercase - characters in the run to appear as capital letters two points smaller - than the font size specified for the run. - """ - return self._get_bool_prop('smallCaps') - - @small_caps.setter - def small_caps(self, value): - self._set_bool_prop('smallCaps', value) - - @property - def snap_to_grid(self): - """ - Read/write tri-state value. When |True| causes the run to use the - document grid characters per line settings defined in the docGrid - element when laying out the characters in this run. - """ - return self._get_bool_prop('snapToGrid') - - @snap_to_grid.setter - def snap_to_grid(self, value): - self._set_bool_prop('snapToGrid', value) - - @property - def spec_vanish(self): - """ - Read/write tri-state value. When |True|, specifies that the given run - shall always behave as if it is hidden, even when hidden text is - being displayed in the current document. The property has a very - narrow, specialized use related to the table of contents. Consult the - spec (§17.3.2.36) for more details. - """ - return self._get_bool_prop('specVanish') - - @spec_vanish.setter - def spec_vanish(self, value): - self._set_bool_prop('specVanish', value) - - @property - def strike(self): - """ - Read/write tri-state value. When |True| causes the text in the run - to appear with a single horizontal line through the center of the - line. - """ - return self._get_bool_prop('strike') - - @strike.setter - def strike(self, value): - self._set_bool_prop('strike', value) - - @property - def subscript(self): - """ - Boolean indicating whether the characters in this |Font| appear as - subscript. |None| indicates the subscript/subscript value is - inherited from the style hierarchy. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.subscript - - @subscript.setter - def subscript(self, value): - rPr = self._element.get_or_add_rPr() - rPr.subscript = value - - @property - def superscript(self): - """ - Boolean indicating whether the characters in this |Font| appear as - superscript. |None| indicates the subscript/superscript value is - inherited from the style hierarchy. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.superscript - - @superscript.setter - def superscript(self, value): - rPr = self._element.get_or_add_rPr() - rPr.superscript = value - - @property - def underline(self): - """ - The underline style for this |Font|, one of |None|, |True|, |False|, - or a value from :ref:`WdUnderline`. |None| indicates the font - inherits its underline value from the style hierarchy. |False| - indicates no underline. |True| indicates single underline. The values - from :ref:`WdUnderline` are used to specify other outline styles such - as double, wavy, and dotted. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.u_val - - @underline.setter - def underline(self, value): - rPr = self._element.get_or_add_rPr() - rPr.u_val = value - - @property - def web_hidden(self): - """ - Read/write tri-state value. When |True|, specifies that the contents - of this run shall be hidden when the document is displayed in web - page view. - """ - return self._get_bool_prop('webHidden') - - @web_hidden.setter - def web_hidden(self, value): - self._set_bool_prop('webHidden', value) - - def _get_bool_prop(self, name): - """ - Return the value of boolean child of `w:rPr` having *name*. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr._get_bool_val(name) - - def _set_bool_prop(self, name, value): - """ - Assign *value* to the boolean child *name* of `w:rPr`. - """ - rPr = self._element.get_or_add_rPr() - rPr._set_bool_val(name, value) diff --git a/build/lib/docx/text/paragraph.py b/build/lib/docx/text/paragraph.py deleted file mode 100644 index eb8e3f66f..000000000 --- a/build/lib/docx/text/paragraph.py +++ /dev/null @@ -1,178 +0,0 @@ -# encoding: utf-8 - -""" -Paragraph-related proxy types. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..enum.style import WD_STYLE_TYPE -from .parfmt import ParagraphFormat -from .run import Run -from ..shared import Parented - -from datetime import datetime - -class Paragraph(Parented): - """ - Proxy object wrapping ```` element. - """ - def __init__(self, p, parent): - super(Paragraph, self).__init__(parent) - self._p = self._element = p - - def add_run(self, text=None, style=None): - """ - Append a run to this paragraph containing *text* and having character - style identified by style ID *style*. *text* can contain tab - (``\\t``) characters, which are converted to the appropriate XML form - for a tab. *text* can also include newline (``\\n``) or carriage - return (``\\r``) characters, each of which is converted to a line - break. - """ - r = self._p.add_r() - run = Run(r, self) - if text: - run.text = text - if style: - run.style = style - return run - - # def add_comment(self, author, initials, dt, comment_text, rangeStart=0, rangeEnd=0): - # comment_part = self.part.comments_part.element - # comment = comment_part.add_comment(author, initials, dt) - # comment._add_p(comment_text) - - # _r = self._p.add_r() - # _r.add_comment_reference(comment._id) - # self._p.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) - - # return comment - - def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0): - comment_part = self.part._comments_part.element - if dtime is None: - dtime = str( datetime.now ) - comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) - - return comment - - def add_footnote(self, text): - footnotes_part = self.part._footnotes_part.element - footnote = self._p.add_fn(text, footnotes_part) - - return footnote - - @property - def alignment(self): - """ - A member of the :ref:`WdParagraphAlignment` enumeration specifying - the justification setting for this paragraph. A value of |None| - indicates the paragraph has no directly-applied alignment value and - will inherit its alignment value from its style hierarchy. Assigning - |None| to this property removes any directly-applied alignment value. - """ - return self._p.alignment - - @alignment.setter - def alignment(self, value): - self._p.alignment = value - - def clear(self): - """ - Return this same paragraph after removing all its content. - Paragraph-level formatting, such as style, is preserved. - """ - self._p.clear_content() - return self - - def insert_paragraph_before(self, text=None, style=None): - """ - Return a newly created paragraph, inserted directly before this - paragraph. If *text* is supplied, the new paragraph contains that - text in a single run. If *style* is provided, that style is assigned - to the new paragraph. - """ - paragraph = self._insert_paragraph_before() - if text: - paragraph.add_run(text) - if style is not None: - paragraph.style = style - return paragraph - - @property - def paragraph_format(self): - """ - The |ParagraphFormat| object providing access to the formatting - properties for this paragraph, such as line spacing and indentation. - """ - return ParagraphFormat(self._element) - - @property - def runs(self): - """ - Sequence of |Run| instances corresponding to the elements in - this paragraph. - """ - return [Run(r, self) for r in self._p.r_lst] - - @property - def style(self): - """ - Read/Write. |_ParagraphStyle| object representing the style assigned - to this paragraph. If no explicit style is assigned to this - paragraph, its value is the default paragraph style for the document. - A paragraph style name can be assigned in lieu of a paragraph style - object. Assigning |None| removes any applied style, making its - effective value the default paragraph style for the document. - """ - style_id = self._p.style - return self.part.get_style(style_id, WD_STYLE_TYPE.PARAGRAPH) - - @style.setter - def style(self, style_or_name): - style_id = self.part.get_style_id( - style_or_name, WD_STYLE_TYPE.PARAGRAPH - ) - self._p.style = style_id - - @property - def text(self): - """ - String formed by concatenating the text of each run in the paragraph. - Tabs and line breaks in the XML are mapped to ``\\t`` and ``\\n`` - characters respectively. - - Assigning text to this property causes all existing paragraph content - to be replaced with a single run containing the assigned text. - A ``\\t`` character in the text is mapped to a ```` element - and each ``\\n`` or ``\\r`` character is mapped to a line break. - Paragraph-level formatting, such as style, is preserved. All - run-level formatting, such as bold or italic, is removed. - """ - text = '' - for run in self.runs: - text += run.text - return text - - @property - def footnotes(self): - if self._p.footnote_ids is not None : - return True - else : - return False - - @text.setter - def text(self, text): - self.clear() - self.add_run(text) - - def _insert_paragraph_before(self): - """ - Return a newly created paragraph, inserted directly before this - paragraph. - """ - p = self._p.add_p_before() - return Paragraph(p, self._parent) diff --git a/build/lib/docx/text/parfmt.py b/build/lib/docx/text/parfmt.py deleted file mode 100644 index 37206729c..000000000 --- a/build/lib/docx/text/parfmt.py +++ /dev/null @@ -1,303 +0,0 @@ -# encoding: utf-8 - -""" -Paragraph-related proxy types. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..enum.text import WD_LINE_SPACING -from ..shared import ElementProxy, Emu, lazyproperty, Length, Pt, Twips -from .tabstops import TabStops - - -class ParagraphFormat(ElementProxy): - """ - Provides access to paragraph formatting such as justification, - indentation, line spacing, space before and after, and widow/orphan - control. - """ - - __slots__ = ('_tab_stops',) - - @property - def alignment(self): - """ - A member of the :ref:`WdParagraphAlignment` enumeration specifying - the justification setting for this paragraph. A value of |None| - indicates paragraph alignment is inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.jc_val - - @alignment.setter - def alignment(self, value): - pPr = self._element.get_or_add_pPr() - pPr.jc_val = value - - @property - def first_line_indent(self): - """ - |Length| value specifying the relative difference in indentation for - the first line of the paragraph. A positive value causes the first - line to be indented. A negative value produces a hanging indent. - |None| indicates first line indentation is inherited from the style - hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.first_line_indent - - @first_line_indent.setter - def first_line_indent(self, value): - pPr = self._element.get_or_add_pPr() - pPr.first_line_indent = value - - @property - def keep_together(self): - """ - |True| if the paragraph should be kept "in one piece" and not broken - across a page boundary when the document is rendered. |None| - indicates its effective value is inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.keepLines_val - - @keep_together.setter - def keep_together(self, value): - self._element.get_or_add_pPr().keepLines_val = value - - @property - def keep_with_next(self): - """ - |True| if the paragraph should be kept on the same page as the - subsequent paragraph when the document is rendered. For example, this - property could be used to keep a section heading on the same page as - its first paragraph. |None| indicates its effective value is - inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.keepNext_val - - @keep_with_next.setter - def keep_with_next(self, value): - self._element.get_or_add_pPr().keepNext_val = value - - @property - def left_indent(self): - """ - |Length| value specifying the space between the left margin and the - left side of the paragraph. |None| indicates the left indent value is - inherited from the style hierarchy. Use an |Inches| value object as - a convenient way to apply indentation in units of inches. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.ind_left - - @left_indent.setter - def left_indent(self, value): - pPr = self._element.get_or_add_pPr() - pPr.ind_left = value - - @property - def line_spacing(self): - """ - |float| or |Length| value specifying the space between baselines in - successive lines of the paragraph. A value of |None| indicates line - spacing is inherited from the style hierarchy. A float value, e.g. - ``2.0`` or ``1.75``, indicates spacing is applied in multiples of - line heights. A |Length| value such as ``Pt(12)`` indicates spacing - is a fixed height. The |Pt| value class is a convenient way to apply - line spacing in units of points. Assigning |None| resets line spacing - to inherit from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return self._line_spacing(pPr.spacing_line, pPr.spacing_lineRule) - - @line_spacing.setter - def line_spacing(self, value): - pPr = self._element.get_or_add_pPr() - if value is None: - pPr.spacing_line = None - pPr.spacing_lineRule = None - elif isinstance(value, Length): - pPr.spacing_line = value - if pPr.spacing_lineRule != WD_LINE_SPACING.AT_LEAST: - pPr.spacing_lineRule = WD_LINE_SPACING.EXACTLY - else: - pPr.spacing_line = Emu(value * Twips(240)) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - - @property - def line_spacing_rule(self): - """ - A member of the :ref:`WdLineSpacing` enumeration indicating how the - value of :attr:`line_spacing` should be interpreted. Assigning any of - the :ref:`WdLineSpacing` members :attr:`SINGLE`, :attr:`DOUBLE`, or - :attr:`ONE_POINT_FIVE` will cause the value of :attr:`line_spacing` - to be updated to produce the corresponding line spacing. - """ - pPr = self._element.pPr - if pPr is None: - return None - return self._line_spacing_rule( - pPr.spacing_line, pPr.spacing_lineRule - ) - - @line_spacing_rule.setter - def line_spacing_rule(self, value): - pPr = self._element.get_or_add_pPr() - if value == WD_LINE_SPACING.SINGLE: - pPr.spacing_line = Twips(240) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - elif value == WD_LINE_SPACING.ONE_POINT_FIVE: - pPr.spacing_line = Twips(360) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - elif value == WD_LINE_SPACING.DOUBLE: - pPr.spacing_line = Twips(480) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - else: - pPr.spacing_lineRule = value - - @property - def page_break_before(self): - """ - |True| if the paragraph should appear at the top of the page - following the prior paragraph. |None| indicates its effective value - is inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.pageBreakBefore_val - - @page_break_before.setter - def page_break_before(self, value): - self._element.get_or_add_pPr().pageBreakBefore_val = value - - @property - def right_indent(self): - """ - |Length| value specifying the space between the right margin and the - right side of the paragraph. |None| indicates the right indent value - is inherited from the style hierarchy. Use a |Cm| value object as - a convenient way to apply indentation in units of centimeters. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.ind_right - - @right_indent.setter - def right_indent(self, value): - pPr = self._element.get_or_add_pPr() - pPr.ind_right = value - - @property - def space_after(self): - """ - |Length| value specifying the spacing to appear between this - paragraph and the subsequent paragraph. |None| indicates this value - is inherited from the style hierarchy. |Length| objects provide - convenience properties, such as :attr:`~.Length.pt` and - :attr:`~.Length.inches`, that allow easy conversion to various length - units. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.spacing_after - - @space_after.setter - def space_after(self, value): - self._element.get_or_add_pPr().spacing_after = value - - @property - def space_before(self): - """ - |Length| value specifying the spacing to appear between this - paragraph and the prior paragraph. |None| indicates this value is - inherited from the style hierarchy. |Length| objects provide - convenience properties, such as :attr:`~.Length.pt` and - :attr:`~.Length.cm`, that allow easy conversion to various length - units. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.spacing_before - - @space_before.setter - def space_before(self, value): - self._element.get_or_add_pPr().spacing_before = value - - @lazyproperty - def tab_stops(self): - """ - |TabStops| object providing access to the tab stops defined for this - paragraph format. - """ - pPr = self._element.get_or_add_pPr() - return TabStops(pPr) - - @property - def widow_control(self): - """ - |True| if the first and last lines in the paragraph remain on the - same page as the rest of the paragraph when Word repaginates the - document. |None| indicates its effective value is inherited from the - style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.widowControl_val - - @widow_control.setter - def widow_control(self, value): - self._element.get_or_add_pPr().widowControl_val = value - - @staticmethod - def _line_spacing(spacing_line, spacing_lineRule): - """ - Return the line spacing value calculated from the combination of - *spacing_line* and *spacing_lineRule*. Returns a |float| number of - lines when *spacing_lineRule* is ``WD_LINE_SPACING.MULTIPLE``, - otherwise a |Length| object of absolute line height is returned. - Returns |None| when *spacing_line* is |None|. - """ - if spacing_line is None: - return None - if spacing_lineRule == WD_LINE_SPACING.MULTIPLE: - return spacing_line / Pt(12) - return spacing_line - - @staticmethod - def _line_spacing_rule(line, lineRule): - """ - Return the line spacing rule value calculated from the combination of - *line* and *lineRule*. Returns special members of the - :ref:`WdLineSpacing` enumeration when line spacing is single, double, - or 1.5 lines. - """ - if lineRule == WD_LINE_SPACING.MULTIPLE: - if line == Twips(240): - return WD_LINE_SPACING.SINGLE - if line == Twips(360): - return WD_LINE_SPACING.ONE_POINT_FIVE - if line == Twips(480): - return WD_LINE_SPACING.DOUBLE - return lineRule diff --git a/build/lib/docx/text/run.py b/build/lib/docx/text/run.py deleted file mode 100644 index 8f2ea787a..000000000 --- a/build/lib/docx/text/run.py +++ /dev/null @@ -1,202 +0,0 @@ -# encoding: utf-8 - -""" -Run-related proxy objects for python-docx, Run in particular. -""" - -from __future__ import absolute_import, print_function, unicode_literals - -from ..enum.style import WD_STYLE_TYPE -from ..enum.text import WD_BREAK -from .font import Font -from ..shape import InlineShape -from ..shared import Parented - - -class Run(Parented): - """ - Proxy object wrapping ```` element. Several of the properties on Run - take a tri-state value, |True|, |False|, or |None|. |True| and |False| - correspond to on and off respectively. |None| indicates the property is - not specified directly on the run and its effective value is taken from - the style hierarchy. - """ - def __init__(self, r, parent): - super(Run, self).__init__(parent) - self._r = self._element = self.element = r - - def add_break(self, break_type=WD_BREAK.LINE): - """ - Add a break element of *break_type* to this run. *break_type* can - take the values `WD_BREAK.LINE`, `WD_BREAK.PAGE`, and - `WD_BREAK.COLUMN` where `WD_BREAK` is imported from `docx.enum.text`. - *break_type* defaults to `WD_BREAK.LINE`. - """ - type_, clear = { - WD_BREAK.LINE: (None, None), - WD_BREAK.PAGE: ('page', None), - WD_BREAK.COLUMN: ('column', None), - WD_BREAK.LINE_CLEAR_LEFT: ('textWrapping', 'left'), - WD_BREAK.LINE_CLEAR_RIGHT: ('textWrapping', 'right'), - WD_BREAK.LINE_CLEAR_ALL: ('textWrapping', 'all'), - }[break_type] - br = self._r.add_br() - if type_ is not None: - br.type = type_ - if clear is not None: - br.clear = clear - - def add_picture(self, image_path_or_stream, width=None, height=None): - """ - Return an |InlineShape| instance containing the image identified by - *image_path_or_stream*, added to the end of this run. - *image_path_or_stream* can be a path (a string) or a file-like object - containing a binary image. If neither width nor height is specified, - the picture appears at its native size. If only one is specified, it - is used to compute a scaling factor that is then applied to the - unspecified dimension, preserving the aspect ratio of the image. The - native size of the picture is calculated using the dots-per-inch - (dpi) value specified in the image file, defaulting to 72 dpi if no - value is specified, as is often the case. - """ - inline = self.part.new_pic_inline(image_path_or_stream, width, height) - self._r.add_drawing(inline) - return InlineShape(inline) - - def add_tab(self): - """ - Add a ```` element at the end of the run, which Word - interprets as a tab character. - """ - self._r._add_tab() - - def add_text(self, text): - """ - Returns a newly appended |_Text| object (corresponding to a new - ```` child element) to the run, containing *text*. Compare with - the possibly more friendly approach of assigning text to the - :attr:`Run.text` property. - """ - t = self._r.add_t(text) - return _Text(t) - - @property - def bold(self): - """ - Read/write. Causes the text of the run to appear in bold. - """ - return self.font.bold - - @bold.setter - def bold(self, value): - self.font.bold = value - - def clear(self): - """ - Return reference to this run after removing all its content. All run - formatting is preserved. - """ - self._r.clear_content() - return self - - @property - def font(self): - """ - The |Font| object providing access to the character formatting - properties for this run, such as font name and size. - """ - return Font(self._element) - - @property - def italic(self): - """ - Read/write tri-state value. When |True|, causes the text of the run - to appear in italics. - """ - return self.font.italic - - @italic.setter - def italic(self, value): - self.font.italic = value - - @property - def style(self): - """ - Read/write. A |_CharacterStyle| object representing the character - style applied to this run. The default character style for the - document (often `Default Character Font`) is returned if the run has - no directly-applied character style. Setting this property to |None| - removes any directly-applied character style. - """ - style_id = self._r.style - return self.part.get_style(style_id, WD_STYLE_TYPE.CHARACTER) - - @style.setter - def style(self, style_or_name): - style_id = self.part.get_style_id( - style_or_name, WD_STYLE_TYPE.CHARACTER - ) - self._r.style = style_id - - @property - def text(self): - """ - String formed by concatenating the text equivalent of each run - content child element into a Python string. Each ```` element - adds the text characters it contains. A ```` element adds - a ``\\t`` character. A ```` or ```` element each add - a ``\\n`` character. Note that a ```` element can indicate - a page break or column break as well as a line break. All ```` - elements translate to a single ``\\n`` character regardless of their - type. All other content child elements, such as ````, are - ignored. - - Assigning text to this property has the reverse effect, translating - each ``\\t`` character to a ```` element and each ``\\n`` or - ``\\r`` character to a ```` element. Any existing run content - is replaced. Run formatting is preserved. - """ - return self._r.text - - @text.setter - def text(self, text): - self._r.text = text - - @property - def underline(self): - """ - The underline style for this |Run|, one of |None|, |True|, |False|, - or a value from :ref:`WdUnderline`. A value of |None| indicates the - run has no directly-applied underline value and so will inherit the - underline value of its containing paragraph. Assigning |None| to this - property removes any directly-applied underline value. A value of - |False| indicates a directly-applied setting of no underline, - overriding any inherited value. A value of |True| indicates single - underline. The values from :ref:`WdUnderline` are used to specify - other outline styles such as double, wavy, and dotted. - """ - return self.font.underline - - @underline.setter - def underline(self, value): - self.font.underline = value - - @property - def footnote(self): - _id = self._r.footnote_id - - if _id is not None: - footnotes_part = self._parent._parent.part._footnotes_part.element - footnote = footnotes_part.get_footnote_by_id(_id) - return footnote.paragraph.text - else: - return None - - -class _Text(object): - """ - Proxy object wrapping ```` element. - """ - def __init__(self, t_elm): - super(_Text, self).__init__() - self._t = t_elm diff --git a/build/lib/docx/text/tabstops.py b/build/lib/docx/text/tabstops.py deleted file mode 100644 index c22b9bc91..000000000 --- a/build/lib/docx/text/tabstops.py +++ /dev/null @@ -1,143 +0,0 @@ -# encoding: utf-8 - -""" -Tabstop-related proxy types. -""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from ..shared import ElementProxy -from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER - - -class TabStops(ElementProxy): - """ - A sequence of |TabStop| objects providing access to the tab stops of - a paragraph or paragraph style. Supports iteration, indexed access, del, - and len(). It is accesed using the :attr:`~.ParagraphFormat.tab_stops` - property of ParagraphFormat; it is not intended to be constructed - directly. - """ - - __slots__ = ('_pPr') - - def __init__(self, element): - super(TabStops, self).__init__(element, None) - self._pPr = element - - def __delitem__(self, idx): - """ - Remove the tab at offset *idx* in this sequence. - """ - tabs = self._pPr.tabs - try: - tabs.remove(tabs[idx]) - except (AttributeError, IndexError): - raise IndexError('tab index out of range') - - if len(tabs) == 0: - self._pPr.remove(tabs) - - def __getitem__(self, idx): - """ - Enables list-style access by index. - """ - tabs = self._pPr.tabs - if tabs is None: - raise IndexError('TabStops object is empty') - tab = tabs.tab_lst[idx] - return TabStop(tab) - - def __iter__(self): - """ - Generate a TabStop object for each of the w:tab elements, in XML - document order. - """ - tabs = self._pPr.tabs - if tabs is not None: - for tab in tabs.tab_lst: - yield TabStop(tab) - - def __len__(self): - tabs = self._pPr.tabs - if tabs is None: - return 0 - return len(tabs.tab_lst) - - def add_tab_stop(self, position, alignment=WD_TAB_ALIGNMENT.LEFT, - leader=WD_TAB_LEADER.SPACES): - """ - Add a new tab stop at *position*, a |Length| object specifying the - location of the tab stop relative to the paragraph edge. A negative - *position* value is valid and appears in hanging indentation. Tab - alignment defaults to left, but may be specified by passing a member - of the :ref:`WdTabAlignment` enumeration as *alignment*. An optional - leader character can be specified by passing a member of the - :ref:`WdTabLeader` enumeration as *leader*. - """ - tabs = self._pPr.get_or_add_tabs() - tab = tabs.insert_tab_in_order(position, alignment, leader) - return TabStop(tab) - - def clear_all(self): - """ - Remove all custom tab stops. - """ - self._pPr._remove_tabs() - - -class TabStop(ElementProxy): - """ - An individual tab stop applying to a paragraph or style. Accessed using - list semantics on its containing |TabStops| object. - """ - - __slots__ = ('_tab') - - def __init__(self, element): - super(TabStop, self).__init__(element, None) - self._tab = element - - @property - def alignment(self): - """ - A member of :ref:`WdTabAlignment` specifying the alignment setting - for this tab stop. Read/write. - """ - return self._tab.val - - @alignment.setter - def alignment(self, value): - self._tab.val = value - - @property - def leader(self): - """ - A member of :ref:`WdTabLeader` specifying a repeating character used - as a "leader", filling in the space spanned by this tab. Assigning - |None| produces the same result as assigning `WD_TAB_LEADER.SPACES`. - Read/write. - """ - return self._tab.leader - - @leader.setter - def leader(self, value): - self._tab.leader = value - - @property - def position(self): - """ - A |Length| object representing the distance of this tab stop from the - inside edge of the paragraph. May be positive or negative. - Read/write. - """ - return self._tab.pos - - @position.setter - def position(self, value): - tab = self._tab - tabs = tab.getparent() - self._tab = tabs.insert_tab_in_order(value, tab.val, tab.leader) - tabs.remove(tab) From 348587b53fe2b820188bad532c43c0f9ebfa7371 Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Thu, 27 Jun 2019 16:07:45 +0300 Subject: [PATCH 24/70] font theme added! --- docx/__init__.py | 2 +- docx/oxml/text/font.py | 40 ++++++++++++++++++++++++++++++++++++++++ docx/oxml/text/parfmt.py | 1 + docx/text/font.py | 19 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 013ddcf0b..c5ea182d6 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.0' +__version__ = '0.2.1' # register custom Part classes with opc package reader diff --git a/docx/oxml/text/font.py b/docx/oxml/text/font.py index 810ec2b30..889eac098 100644 --- a/docx/oxml/text/font.py +++ b/docx/oxml/text/font.py @@ -32,6 +32,8 @@ class CT_Fonts(BaseOxmlElement): """ ascii = OptionalAttribute('w:ascii', ST_String) hAnsi = OptionalAttribute('w:hAnsi', ST_String) + asciiTheme = OptionalAttribute('w:asciiTheme', ST_String) + hAnsiTheme = OptionalAttribute('w:hAnsiTheme', ST_String) class CT_Highlight(BaseOxmlElement): @@ -155,6 +157,44 @@ def rFonts_hAnsi(self, value): rFonts = self.get_or_add_rFonts() rFonts.hAnsi = value + @property + def rFonts_asciiTheme(self): + """ + The value of `w:rFonts/@w:asciiTheme` or |None| if not present. Represents + the assigned typeface Theme. The rFonts element also specifies other + special-case typeface Theme; this method handles the case where just + the common Theme is required. + """ + rFonts = self.rFonts + if rFonts is None: + return None + return rFonts.asciiTheme + + @rFonts_asciiTheme.setter + def rFonts_asciiTheme(self, value): + if value is None: + self._remove_rFonts() + return + rFonts = self.get_or_add_rFonts() + rFonts.asciiTheme = value + + @property + def rFonts_hAnsiTheme(self): + """ + The value of `w:rFonts/@w:hAnsiTheme` or |None| if not present. + """ + rFonts = self.rFonts + if rFonts is None: + return None + return rFonts.hAnsiTheme + + @rFonts_hAnsiTheme.setter + def rFonts_hAnsiTheme(self, value): + if value is None and self.rFonts is None: + return + rFonts = self.get_or_add_rFonts() + rFonts.hAnsiTheme = value + @property def style(self): """ diff --git a/docx/oxml/text/parfmt.py b/docx/oxml/text/parfmt.py index 466b11b1b..69900b0dc 100644 --- a/docx/oxml/text/parfmt.py +++ b/docx/oxml/text/parfmt.py @@ -57,6 +57,7 @@ class CT_PPr(BaseOxmlElement): spacing = ZeroOrOne('w:spacing', successors=_tag_seq[22:]) ind = ZeroOrOne('w:ind', successors=_tag_seq[23:]) jc = ZeroOrOne('w:jc', successors=_tag_seq[27:]) + rPr = ZeroOrOne('w:rPr', successors=_tag_seq[34:]) sectPr = ZeroOrOne('w:sectPr', successors=_tag_seq[35:]) del _tag_seq diff --git a/docx/text/font.py b/docx/text/font.py index 162832101..bdef5b808 100644 --- a/docx/text/font.py +++ b/docx/text/font.py @@ -197,6 +197,25 @@ def name(self, value): rPr.rFonts_ascii = value rPr.rFonts_hAnsi = value + @property + def theme(self): + """ + Get or set the typeface theme for this |Font| instance, causing the + text it controls to appear in the themed font, if a matching font is + found. |None| indicates the typeface is inherited from the style + hierarchy. + """ + rPr = self._element.rPr + if rPr is None: + return None + return rPr.rFonts_asciiTheme + + @theme.setter + def theme(self, value): + rPr = self._element.get_or_add_rPr() + rPr.rFonts_asciiTheme = value + rPr.rFonts_hAnsiTheme = value + @property def no_proof(self): """ From 8d325eb701f9dc9ad79f73c1aaa933f6085dd5ea Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Thu, 27 Jun 2019 16:09:04 +0300 Subject: [PATCH 25/70] docDefalts Styles added! --- docx/oxml/__init__.py | 5 ++++- docx/oxml/styles.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 349752868..e564b1ab0 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -114,8 +114,11 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('wp:extent', CT_PositiveSize2D) register_element_cls('wp:inline', CT_Inline) -from .styles import CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles +from .styles import CT_DocDefaults, CT_RPrDefault, CT_PPrDefault, CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles register_element_cls('w:basedOn', CT_String) +register_element_cls('w:docDefaults', CT_DocDefaults) +register_element_cls('w:rPrDefault', CT_RPrDefault) +register_element_cls('w:pPrDefault', CT_PPrDefault) register_element_cls('w:latentStyles', CT_LatentStyles) register_element_cls('w:locked', CT_OnOff) register_element_cls('w:lsdException', CT_LsdException) diff --git a/docx/oxml/styles.py b/docx/oxml/styles.py index 6f27e45eb..4a7483bae 100644 --- a/docx/oxml/styles.py +++ b/docx/oxml/styles.py @@ -31,6 +31,17 @@ def styleId_from_name(name): }.get(name, name.replace(' ', '')) +class CT_DocDefaults(BaseOxmlElement): + _tag_seq = ('w:rPrDefault', 'w:pPrDefault') + rPrDefault = ZeroOrOne('w:rPrDefault', successors=(_tag_seq[1:])) + pPrDefault = ZeroOrOne('w:pPrDefault', successors=()) + +class CT_RPrDefault(BaseOxmlElement): + rPr = ZeroOrOne('w:rPr', successors=()) + +class CT_PPrDefault(BaseOxmlElement): + pPr = ZeroOrOne('w:pPr', successors=()) + class CT_LatentStyles(BaseOxmlElement): """ `w:latentStyles` element, defining behavior defaults for latent styles @@ -292,6 +303,7 @@ class CT_Styles(BaseOxmlElement): styles.xml """ _tag_seq = ('w:docDefaults', 'w:latentStyles', 'w:style') + docDefaults = ZeroOrOne('w:docDefaults', successors=_tag_seq[1:]) latentStyles = ZeroOrOne('w:latentStyles', successors=_tag_seq[2:]) style = ZeroOrMore('w:style', successors=()) del _tag_seq From 720678d1f2b4a07373cc2d540d57b992bdd2df72 Mon Sep 17 00:00:00 2001 From: Bassel Al Madani Date: Thu, 27 Jun 2019 16:14:46 +0300 Subject: [PATCH 26/70] update version --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index c5ea182d6..0a649cbb6 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.1' +__version__ = '0.2.2' # register custom Part classes with opc package reader From 6ea78ec0027ede0cda0f2e7fa86cdd8f9c20e431 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 4 Jul 2019 16:56:15 +0300 Subject: [PATCH 27/70] get tables section --- docx/oxml/section.py | 2 +- docx/oxml/table.py | 16 +++++++++++++++- docx/parts/document.py | 2 +- docx/table.py | 5 ++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docx/oxml/section.py b/docx/oxml/section.py index fc953e74d..fd889aa48 100644 --- a/docx/oxml/section.py +++ b/docx/oxml/section.py @@ -54,7 +54,6 @@ class CT_PageSz(BaseOxmlElement): 'w:orient', WD_ORIENTATION, default=WD_ORIENTATION.PORTRAIT ) - class CT_SectPr(BaseOxmlElement): """`w:sectPr` element, the container element for section properties""" @@ -92,6 +91,7 @@ def add_headerReference(self, type_, rId): headerReference.rId = rId return headerReference + @property def bottom_margin(self): """ diff --git a/docx/oxml/table.py b/docx/oxml/table.py index a9f3dbba7..387b05063 100644 --- a/docx/oxml/table.py +++ b/docx/oxml/table.py @@ -10,7 +10,7 @@ from . import OxmlElement from ..enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE from ..exceptions import InvalidSpanError -from .ns import nsdecls, qn +from .ns import nsdecls, qn, nsmap from ..shared import Emu, Twips from .simpletypes import ( ST_Merge, ST_TblLayoutType, ST_TblWidth, ST_TwipsMeasure, XsdInt, ST_String @@ -234,6 +234,20 @@ def _tcs_xml(cls, col_count, col_width): ) % col_width.twips return xml + @property + def _section(self): + body = self.getparent() + sections = body.findall('.//w:sectPr', {'w':nsmap['w']}) + if len(sections) == 1: + return sections[0] + else: + tbl_index = body.index(self) + for i,sect in enumerate(sections): + if i == len(sections) - 1 : + return sect + else: + if body.index(sect.getparent().getparent()) > tbl_index: + return sect class CT_TblGrid(BaseOxmlElement): """ diff --git a/docx/parts/document.py b/docx/parts/document.py index df6b0869b..fe55eb3a6 100644 --- a/docx/parts/document.py +++ b/docx/parts/document.py @@ -8,7 +8,7 @@ from docx.opc.constants import RELATIONSHIP_TYPE as RT from docx.parts.hdrftr import FooterPart, HeaderPart from docx.parts.numbering import NumberingPart -from docx.parts.settings import SettingsParts +from docx.parts.settings import SettingsPart from docx.parts.story import BaseStoryPart from docx.parts.styles import StylesPart from docx.parts.comments import CommentsPart diff --git a/docx/table.py b/docx/table.py index b3bc090fb..9801f8346 100644 --- a/docx/table.py +++ b/docx/table.py @@ -10,7 +10,7 @@ from .enum.style import WD_STYLE_TYPE from .oxml.simpletypes import ST_Merge from .shared import Inches, lazyproperty, Parented - +from .section import Section class Table(Parented): """ @@ -45,6 +45,9 @@ def add_row(self): return _Row(tr, self) @property + def section(self): + return Section(self._element._section, self.part) + @property def alignment(self): """ Read/write. A member of :ref:`WdRowAlignment` or None, specifying the From c6714545237b6f5801bb1fc36b536010e6bb56df Mon Sep 17 00:00:00 2001 From: obay daba Date: Sun, 14 Jul 2019 01:13:08 +0300 Subject: [PATCH 28/70] add element property for document object --- docx/blkcntnr.py | 19 +++++++++++++++++++ docx/document.py | 4 +++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docx/blkcntnr.py b/docx/blkcntnr.py index a80903e52..2cd39b6c8 100644 --- a/docx/blkcntnr.py +++ b/docx/blkcntnr.py @@ -66,6 +66,14 @@ def tables(self): """ from .table import Table return [Table(tbl, self) for tbl in self._element.tbl_lst] + @property + def elements(self): + """ + A list containing the elements in this container (paragraph and tables), in document order. + """ + #pass + return [element(item,self.part) for item in self._element.getchildren()] + def _add_paragraph(self): """ @@ -73,3 +81,14 @@ def _add_paragraph(self): container. """ return Paragraph(self._element.add_p(), self) + + +def element(element, part): + if str(type(element)) == "": + return Paragraph(element, element.getparent()) + elif str(type(element)) == "": + from .table import Table + return Table(element, element.getparent()) + elif str(type(element)) == "": + from .section import Section + return Section(element, part) \ No newline at end of file diff --git a/docx/document.py b/docx/document.py index 592f076cf..56a6ff448 100644 --- a/docx/document.py +++ b/docx/document.py @@ -118,7 +118,6 @@ def comments_part(self): # return self.part._footnotes_part - @property def inline_shapes(self): """ @@ -184,6 +183,9 @@ def tables(self): return self._body.tables @property + def elements(self): + return self._body.elements + @property def _block_width(self): """ Return a |Length| object specifying the width of available "writing" From 8319e10501ba1d5e815ac563be3267fc1ad4932b Mon Sep 17 00:00:00 2001 From: obay daba Date: Mon, 15 Jul 2019 23:19:17 +0300 Subject: [PATCH 29/70] added element property to document object --- docx/api.py | 11 +++++++++++ docx/blkcntnr.py | 12 +----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docx/api.py b/docx/api.py index 63e18c406..6ba865bb1 100644 --- a/docx/api.py +++ b/docx/api.py @@ -35,3 +35,14 @@ def _default_docx_path(): """ _thisdir = os.path.split(__file__)[0] return os.path.join(_thisdir, 'templates', 'default.docx') + + +def element(element, part): + if str(type(element)) == "": + return Paragraph(element, element.getparent()) + elif str(type(element)) == "": + from .table import Table + return Table(element, element.getparent()) + elif str(type(element)) == "": + from .section import Section + return Section(element, part) \ No newline at end of file diff --git a/docx/blkcntnr.py b/docx/blkcntnr.py index 2cd39b6c8..2060fac17 100644 --- a/docx/blkcntnr.py +++ b/docx/blkcntnr.py @@ -11,7 +11,7 @@ from docx.oxml.table import CT_Tbl from docx.shared import Parented from docx.text.paragraph import Paragraph - +from docx.api import element class BlockItemContainer(Parented): """Base class for proxy objects that can contain block items. @@ -82,13 +82,3 @@ def _add_paragraph(self): """ return Paragraph(self._element.add_p(), self) - -def element(element, part): - if str(type(element)) == "": - return Paragraph(element, element.getparent()) - elif str(type(element)) == "": - from .table import Table - return Table(element, element.getparent()) - elif str(type(element)) == "": - from .section import Section - return Section(element, part) \ No newline at end of file From 15aa87bf5c15f38ffdecfe116fdfd8e4f2b7c299 Mon Sep 17 00:00:00 2001 From: obay daba Date: Tue, 3 Sep 2019 14:44:58 +0300 Subject: [PATCH 30/70] add abstractNumIds as attrib for document object --- docx/blkcntnr.py | 9 +++++++-- docx/document.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docx/blkcntnr.py b/docx/blkcntnr.py index 2060fac17..5c9810060 100644 --- a/docx/blkcntnr.py +++ b/docx/blkcntnr.py @@ -9,6 +9,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from docx.oxml.table import CT_Tbl +from docx.oxml.ns import qn from docx.shared import Parented from docx.text.paragraph import Paragraph from docx.api import element @@ -71,10 +72,14 @@ def elements(self): """ A list containing the elements in this container (paragraph and tables), in document order. """ - #pass return [element(item,self.part) for item in self._element.getchildren()] - + + + @property + def abstractNumIds(self): + return [numId for numId in self.part.numbering_part.element.iterchildren(qn('w:abstractNum'))] + def _add_paragraph(self): """ Return a paragraph newly added to the end of the content in this diff --git a/docx/document.py b/docx/document.py index 56a6ff448..a35c85b72 100644 --- a/docx/document.py +++ b/docx/document.py @@ -4,6 +4,8 @@ from __future__ import absolute_import, division, print_function, unicode_literals +from docx.oxml.ns import qn + from docx.blkcntnr import BlockItemContainer from docx.enum.section import WD_SECTION from docx.enum.text import WD_BREAK @@ -185,6 +187,20 @@ def tables(self): @property def elements(self): return self._body.elements + + @property + def abstractNumIds(self): + """ + Returns list of all the 'w:abstarctNumId' of this document + """ + return self._body.abstractNumIds + + @property + def last_abs_num(self): + last = self.abstractNumIds[-1] + val = last.attrib.get(qn('w:abstractNumId')) + return last, val + @property def _block_width(self): """ From 9965f2c04955611c9402427f53e7df134b237250 Mon Sep 17 00:00:00 2001 From: obay daba Date: Tue, 3 Sep 2019 20:30:06 +0300 Subject: [PATCH 31/70] new attribs for Paragraph & Run --- docx/__init__.py | 2 +- docx/text/paragraph.py | 84 ++++++++++++++++++++++++++++++++++++------ docx/text/run.py | 32 ++++++++++++++++ 3 files changed, 105 insertions(+), 13 deletions(-) diff --git a/docx/__init__.py b/docx/__init__.py index 92b939b28..faa2d986d 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.3' #merge with upstream 0.8.10 +__version__ = '0.2.4' #merge with upstream 0.8.10 # register custom Part classes with opc package reader diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index eb8e3f66f..7c80849da 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -14,6 +14,7 @@ from ..shared import Parented from datetime import datetime +import re class Paragraph(Parented): """ @@ -39,18 +40,14 @@ def add_run(self, text=None, style=None): if style: run.style = style return run - - # def add_comment(self, author, initials, dt, comment_text, rangeStart=0, rangeEnd=0): - # comment_part = self.part.comments_part.element - # comment = comment_part.add_comment(author, initials, dt) - # comment._add_p(comment_text) - - # _r = self._p.add_r() - # _r.add_comment_reference(comment._id) - # self._p.link_comment(comment._id, rangeStart= rangeStart, rangeEnd=rangeEnd) - - # return comment - + + def delete(self): + """ + delete the content of the paragraph + """ + self._p.getparent().remove(self._p) + self._p = self._element = None + def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0): comment_part = self.part._comments_part.element if dtime is None: @@ -65,6 +62,16 @@ def add_footnote(self, text): return footnote + def merge_paragraph(self, otherParagraph): + r_lst = otherParagraph.runs + self.merge_runs(r_lst) + + def merge_runs(self, runs): + self.add_run(' ') + for run in runs: + self._p.append(run._r) + + @property def alignment(self): """ @@ -157,6 +164,59 @@ def text(self): text += run.text return text + @property + def header_level(self): + ''' + input Paragraph Object + output Paragraph level in case of header or returns None + ''' + headerPattern = re.compile(".*Heading (\d+)$") + level = None + if headerPattern.match(self.style.name): + level = int(self.style.name.lower().split('heading')[-1].strip()) + return level + + @property + def NumId(self): + ''' + returns NumId val in case of paragraph has numbering + else: return None + ''' + try: + return self._p.pPr.numPr.numId.val + except: + return None + + @property + def list_lvl(self): + ''' + returns ilvl val in case of paragraph has a numbering level + else: return None + ''' + try: + return self._p.pPr.numPr.ilvl.val + except : + return None + + @property + def list_info(self): + ''' + returns tuple (has numbering info, numId value, ilvl value) + ''' + if self.NumId and self.list_lvl: + return True, self.NumId, self.list_lvl + else: + return False, 0, 0 + + @property + def is_heading(self): + return True if self.header_level else False + + @property + def full_text(self): + allRuns = [Run(r, self) for r in self._p.xpath('.//w:r[not(ancestor::w:r)]')] + return u"".join([r.text for r in allRuns]) + @property def footnotes(self): if self._p.footnote_ids is not None : diff --git a/docx/text/run.py b/docx/text/run.py index 8f2ea787a..debaa9364 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -6,6 +6,8 @@ from __future__ import absolute_import, print_function, unicode_literals +from docx.oxml.ns import qn + from ..enum.style import WD_STYLE_TYPE from ..enum.text import WD_BREAK from .font import Font @@ -192,6 +194,36 @@ def footnote(self): else: return None + @property + def is_hyperlink(self): + ''' + checks if the run is nested inside a hyperlink element + ''' + return self.element.getparent().tag.split('}')[1] == 'hyperlink' + + @property + def get_HyperLink(self): + """ + returns the text of the hyperlink of the run in case of the run has a hyperlink + """ + document = self._parent._parent._parent + parent = self.element.getparent() + linkText = '' + if self.is_hyperlink: + if parent.attrib.__contains__(qn('r:id')): + rId = parent.get(qn('r:id')) + linkText = document._part._rels[rId].target_ref + return linkText, True + elif parent.attrib.__contains__(qn('w:anchor')): + linkText = parent.get(qn('w:anchor')) + return linkText, False + else: + print('No Link in Hyperlink!') + print(self.text) + return '', False + else: + return 'None' + class _Text(object): """ From e68f53b788dc47edd612a54d5eef2bf7c15020a6 Mon Sep 17 00:00:00 2001 From: obay daba Date: Fri, 6 Sep 2019 23:58:25 +0300 Subject: [PATCH 32/70] typos --- docx/text/paragraph.py | 4 ++-- docx/text/run.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 7c80849da..9cd2ac9a9 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -66,7 +66,7 @@ def merge_paragraph(self, otherParagraph): r_lst = otherParagraph.runs self.merge_runs(r_lst) - def merge_runs(self, runs): + def append_runs(self, runs): self.add_run(' ') for run in runs: self._p.append(run._r) @@ -171,7 +171,7 @@ def header_level(self): output Paragraph level in case of header or returns None ''' headerPattern = re.compile(".*Heading (\d+)$") - level = None + level = 0 if headerPattern.match(self.style.name): level = int(self.style.name.lower().split('heading')[-1].strip()) return level diff --git a/docx/text/run.py b/docx/text/run.py index debaa9364..360ad83cb 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -201,12 +201,11 @@ def is_hyperlink(self): ''' return self.element.getparent().tag.split('}')[1] == 'hyperlink' - @property - def get_HyperLink(self): + def get_hyperLink(self): """ returns the text of the hyperlink of the run in case of the run has a hyperlink """ - document = self._parent._parent._parent + document = self._parent._parent.document parent = self.element.getparent() linkText = '' if self.is_hyperlink: From 5aadfab5f497f841397a9d681f974047d7c71056 Mon Sep 17 00:00:00 2001 From: obay daba Date: Fri, 6 Sep 2019 23:58:44 +0300 Subject: [PATCH 33/70] fix- parent type --- docx/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docx/api.py b/docx/api.py index 6ba865bb1..1cc5fad34 100644 --- a/docx/api.py +++ b/docx/api.py @@ -39,10 +39,11 @@ def _default_docx_path(): def element(element, part): if str(type(element)) == "": - return Paragraph(element, element.getparent()) + from .text.paragraph import Paragraph + return Paragraph(element, part) elif str(type(element)) == "": from .table import Table - return Table(element, element.getparent()) + return Table(element, part) elif str(type(element)) == "": from .section import Section return Section(element, part) \ No newline at end of file From 3e8ca7db94e27bf1656f01abe9db39cf2b8ed76b Mon Sep 17 00:00:00 2001 From: obay daba Date: Sat, 7 Sep 2019 00:01:25 +0300 Subject: [PATCH 34/70] ignores --- .gitignore | 6 +++++- python_docx-0.8.7-py3-none-any.whl | Bin 247343 -> 0 bytes 2 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 python_docx-0.8.7-py3-none-any.whl diff --git a/.gitignore b/.gitignore index a0b9a4f37..9ce46ccb8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,8 @@ _scratch/ Session.vim /.tox/ -/build/ \ No newline at end of file +/build/ +/tests/ +/features/ +/docs/ +/ref/ \ No newline at end of file diff --git a/python_docx-0.8.7-py3-none-any.whl b/python_docx-0.8.7-py3-none-any.whl deleted file mode 100644 index cabbf44354fa2fbacdcc9967e5d2c2750d260e21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 247343 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 sU__zbZ%2|{~fMIsE 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 z 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

  • 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�D5z$rn2lGL1R#pOL$(-7#1t&E1K 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 z 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 From ee601901e8f3d4ab334302a8d41814f8156c0036 Mon Sep 17 00:00:00 2001 From: obay daba Date: Sat, 7 Sep 2019 00:25:10 +0300 Subject: [PATCH 35/70] 0.2.4 --- HISTORY.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 52cafc928..f5509a587 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,13 @@ Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) --------------- +0.2.4 (2019-9-4) + +- loop over all the document chieldern (Paragraphs, Tables, Sections) with the right order `document.elements` + +- addons to Paragraph Object (delete, heading_level, merge_paragraph ) + + 0.1.3 (2019-03-08) ++++++++++++++++++ From ca46e5784027497125b055ac4f9b0283c608373b Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 26 Sep 2019 11:25:11 +0300 Subject: [PATCH 36/70] append paragraph works --- docx/__init__.py | 2 +- docx/text/paragraph.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docx/__init__.py b/docx/__init__.py index faa2d986d..63b931fb8 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.4' #merge with upstream 0.8.10 +__version__ = '0.2.5' # register custom Part classes with opc package reader diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 9cd2ac9a9..1800fda06 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -64,7 +64,7 @@ def add_footnote(self, text): def merge_paragraph(self, otherParagraph): r_lst = otherParagraph.runs - self.merge_runs(r_lst) + self.append_runs(r_lst) def append_runs(self, runs): self.add_run(' ') From ff9e3a3df22beff25635ae6197e552ac0ad7d759 Mon Sep 17 00:00:00 2001 From: tareq ibrahim Date: Sun, 19 Jan 2020 18:14:06 +0200 Subject: [PATCH 37/70] translate tag: "w:noBreakHyphen" to "-" --- docx/oxml/text/run.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index a37709e96..e93575e52 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -145,6 +145,8 @@ def text(self): text += '\t' elif child.tag in (qn('w:br'), qn('w:cr')): text += '\n' + elif child.tag == qn('w:noBreakHyphen'): + text += '-' return text @text.setter From 8d5fb53c8b725830c10bf3b2f343182ad2f960e3 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sun, 19 Jan 2020 18:27:16 +0200 Subject: [PATCH 38/70] add CONTRIBUTORS.md --- CONTRIBUTORS.md | 10 ++++++++++ docx/__init__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTORS.md diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..621b58882 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,10 @@ +Bayoo-docx contributors (sorted alphabetically) + +============================================ + +* **[Bassel Al Madani](https://github.com/pepos9)** + +* **[Obay Daba](https://github.com/bayoog)** + +* **[Tareq Ibrahim](https://github.com/idtareq)** + diff --git a/docx/__init__.py b/docx/__init__.py index 63b931fb8..1634b762f 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.5' +__version__ = '0.2.6' # register custom Part classes with opc package reader From 8d74621420542f394bb5bee35dfabfa2bff70748 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 1 May 2020 23:03:06 +0300 Subject: [PATCH 39/70] comment datetime --- docx/text/paragraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 1800fda06..38d2a4e39 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -51,7 +51,7 @@ def delete(self): def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0): comment_part = self.part._comments_part.element if dtime is None: - dtime = str( datetime.now ) + dtime = str( datetime.now() ).replace(' ', 'T') comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) return comment From 45c568144bea082b4877f27569fcd72d766c88e8 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 1 May 2020 23:25:26 +0300 Subject: [PATCH 40/70] update readme --- HISTORY.rst | 8 ++++++++ README.rst | 26 ++++++++++++++++++++++++-- docx/__init__.py | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index f5509a587..d9a50af5e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,7 +3,15 @@ Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) --------------- + +0.2.7 (2020-05-01) ++++++++++++++++++++ + +- fix issue with comments date (comments dates are set to current date) + + 0.2.4 (2019-9-4) ++++++++++++++++++++ - loop over all the document chieldern (Paragraphs, Tables, Sections) with the right order `document.elements` diff --git a/README.rst b/README.rst index 5d55db2b9..b8a5e3f3c 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,27 @@ +# Bayoo-docx -*bayoo-docx* is a Python library for creating and updating Microsoft Word -(.docx) files. +Python library forked from [python-docx](https://github.com/python-openxml/python-docx/). +## Installation +Use the package manager [pip](https://pypi.org/project/bayoo-docx/) to install bayoo-docx. + +```bash +pip install bayoo-docx +``` + + +## Usage + +```python +import docx + +document = docx.Document() +paragraph = document.add_paragraph('text') # create new paragraph +comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # create a comment' +paragraph.add_footnote('footnote text') # add a footnote +``` + + +## License +[MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file diff --git a/docx/__init__.py b/docx/__init__.py index 1634b762f..4d7379f4d 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.6' +__version__ = '0.2.7' # register custom Part classes with opc package reader From 1c514307265bc238f4976c4611ca3167333d368d Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 1 May 2020 23:31:33 +0300 Subject: [PATCH 41/70] readme --- README.rst | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index b8a5e3f3c..c9e3348c1 100644 --- a/README.rst +++ b/README.rst @@ -1,27 +1,31 @@ -# Bayoo-docx + # Bayoo-docx -Python library forked from [python-docx](https://github.com/python-openxml/python-docx/). + Python library forked from [python-docx](https://github.com/python-openxml/python-docx/). -## Installation + ## Installation -Use the package manager [pip](https://pypi.org/project/bayoo-docx/) to install bayoo-docx. + Use the package manager [pip](https://pypi.org/project/bayoo-docx/) to install bayoo-docx. -```bash -pip install bayoo-docx -``` + ```bash + pip install bayoo-docx + ``` + ## Features + + The main purpose of forking [python-docx](https://github.com/python-openxml/python-docx/) was to add comments and footnotes implementation + - low-level support for comments & footnotes (on oxml level) -## Usage + ## Usage -```python -import docx + ```python + import docx -document = docx.Document() -paragraph = document.add_paragraph('text') # create new paragraph -comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # create a comment' -paragraph.add_footnote('footnote text') # add a footnote -``` + document = docx.Document() + paragraph = document.add_paragraph('text') # create new paragraph + comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # create a comment' + paragraph.add_footnote('footnote text') # add a footnote + ``` -## License -[MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file + ## License + [MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file From 6204f12cf48784f17ddeaee69d4ab7704a77aafb Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 01:30:57 +0300 Subject: [PATCH 42/70] fix rst formating --- HISTORY.rst | 11 +---------- README.rst | 38 +++++++++++++++++++++----------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index d9a50af5e..0db463d89 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,7 +2,7 @@ Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) ---------------- +-------------------------------------------------------------------------- 0.2.7 (2020-05-01) +++++++++++++++++++ @@ -14,14 +14,7 @@ Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) +++++++++++++++++++ - loop over all the document chieldern (Paragraphs, Tables, Sections) with the right order `document.elements` - - addons to Paragraph Object (delete, heading_level, merge_paragraph ) - - - -0.1.3 (2019-03-08) -++++++++++++++++++ - - Add low-level implementation for comments part - Add oxml element for element and sub-elements - Add add_comment() method for docx.text.Paragraph @@ -32,8 +25,6 @@ Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) - Add add_footnote() method for docx.text.Paragraph -Release History python-openxmm/python-docx ---------------- 0.8.10 (2019-01-08) +++++++++++++++++++ diff --git a/README.rst b/README.rst index c9e3348c1..a4e846efa 100644 --- a/README.rst +++ b/README.rst @@ -1,31 +1,35 @@ - # Bayoo-docx +Bayoo-docx +=========== - Python library forked from [python-docx](https://github.com/python-openxml/python-docx/). +Python library forked from `python-docx `_. - ## Installation - Use the package manager [pip](https://pypi.org/project/bayoo-docx/) to install bayoo-docx. - ```bash - pip install bayoo-docx - ``` +Installation +------------ - ## Features - - The main purpose of forking [python-docx](https://github.com/python-openxml/python-docx/) was to add comments and footnotes implementation - - low-level support for comments & footnotes (on oxml level) +Use the package manager `pip `_ to install bayoo-docx. - ## Usage - ```python - import docx +`pip install bayoo-docx` +Usage +----- +:: + + import docx + document = docx.Document() + paragraph = document.add_paragraph('text') # create new paragraph + comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # create a comment' + paragraph.add_footnote('footnote text') # add a footnote - ``` - ## License - [MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file + +License +-------------- + +`MIT `_ \ No newline at end of file From cf8df47bb9066ac50c6ee9c5e378e01ae12d6e9d Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 01:33:35 +0300 Subject: [PATCH 43/70] note in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a4e846efa..9a104f2d8 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Bayoo-docx Python library forked from `python-docx `_. - +The main purpose of the fork was to add implementation for comments and footnotes to the library Installation ------------ From 94266f2af2603090cf1c4577d7e5c4c20da670c9 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 02:31:20 +0300 Subject: [PATCH 44/70] pypi rst format --- HISTORY.rst | 273 +--------------------------------------------------- README.rst | 16 ++- 2 files changed, 12 insertions(+), 277 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 0db463d89..2ef32df8f 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,287 +1,24 @@ .. :changelog: -Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) --------------------------------------------------------------------------- +#Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) + + +###0.2.7 (2020-05-01) -0.2.7 (2020-05-01) -+++++++++++++++++++ - fix issue with comments date (comments dates are set to current date) -0.2.4 (2019-9-4) -+++++++++++++++++++ +###0.2.4 (2019-9-4) - loop over all the document chieldern (Paragraphs, Tables, Sections) with the right order `document.elements` - addons to Paragraph Object (delete, heading_level, merge_paragraph ) - Add low-level implementation for comments part - Add oxml element for element and sub-elements - Add add_comment() method for docx.text.Paragraph - - - Add low-level implementation for footnotes part - Add oxml element for element and sub-elements - Add add_footnote() method for docx.text.Paragraph - -0.8.10 (2019-01-08) -+++++++++++++++++++ - -- Revert use of expanded package directory for default.docx to work around setup.py - problem with filenames containing square brackets. - - -0.8.9 (2019-01-08) -++++++++++++++++++ - -- Fix gap in MANIFEST.in that excluded default document template directory - - -0.8.8 (2019-01-07) -++++++++++++++++++ - -- Add support for headers and footers - - -0.8.7 (2018-08-18) -++++++++++++++++++ - -- Add _Row.height_rule -- Add _Row.height -- Add _Cell.vertical_alignment -- Fix #455: increment next_id, don't fill gaps -- Add #375: import docx failure on --OO optimization -- Add #254: remove default zoom percentage -- Add #266: miscellaneous documentation fixes -- Add #175: refine MANIFEST.ini -- Add #168: Unicode error on core-props in Python 2 - - -0.8.6 (2016-06-22) -++++++++++++++++++ - -- Add #257: add Font.highlight_color -- Add #261: add ParagraphFormat.tab_stops -- Add #303: disallow XML entity expansion - - -0.8.5 (2015-02-21) -++++++++++++++++++ - -- Fix #149: KeyError on Document.add_table() -- Fix #78: feature: add_table() sets cell widths -- Add #106: feature: Table.direction (i.e. right-to-left) -- Add #102: feature: add CT_Row.trPr - - -0.8.4 (2015-02-20) -++++++++++++++++++ - -- Fix #151: tests won't run on PyPI distribution -- Fix #124: default to inches on no TIFF resolution unit - - -0.8.3 (2015-02-19) -++++++++++++++++++ - -- Add #121, #135, #139: feature: Font.color - - -0.8.2 (2015-02-16) -++++++++++++++++++ - -- Fix #94: picture prints at wrong size when scaled -- Extract `docx.document.Document` object from `DocumentPart` - - Refactor `docx.Document` from an object into a factory function for new - `docx.document.Document object`. Extract methods from prior `docx.Document` - and `docx.parts.document.DocumentPart` to form the new API class and retire - `docx.Document` class. - -- Migrate `Document.numbering_part` to `DocumentPart.numbering_part`. The - `numbering_part` property is not part of the published API and is an - interim internal feature to be replaced in a future release, perhaps with - something like `Document.numbering_definitions`. In the meantime, it can - now be accessed using ``Document.part.numbering_part``. - - -0.8.1 (2015-02-10) -++++++++++++++++++ - -- Fix #140: Warning triggered on Document.add_heading/table() - - -0.8.0 (2015-02-08) -++++++++++++++++++ - -- Add styles. Provides general capability to access and manipulate paragraph, - character, and table styles. - -- Add ParagraphFormat object, accessible on Paragraph.paragraph_format, and - providing the following paragraph formatting properties: - - + paragraph alignment (justfification) - + space before and after paragraph - + line spacing - + indentation - + keep together, keep with next, page break before, and widow control - -- Add Font object, accessible on Run.font, providing character-level - formatting including: - - + typeface (e.g. 'Arial') - + point size - + underline - + italic - + bold - + superscript and subscript - -The following issues were retired: - -- Add feature #56: superscript/subscript -- Add feature #67: lookup style by UI name -- Add feature #98: Paragraph indentation -- Add feature #120: Document.styles - -**Backward incompatibilities** - -Paragraph.style now returns a Style object. Previously it returned the style -name as a string. The name can now be retrieved using the Style.name -property, for example, `paragraph.style.name`. - - -0.7.6 (2014-12-14) -++++++++++++++++++ - -- Add feature #69: Table.alignment -- Add feature #29: Document.core_properties - - -0.7.5 (2014-11-29) -++++++++++++++++++ - -- Add feature #65: _Cell.merge() - - -0.7.4 (2014-07-18) -++++++++++++++++++ - -- Add feature #45: _Cell.add_table() -- Add feature #76: _Cell.add_paragraph() -- Add _Cell.tables property (read-only) - - -0.7.3 (2014-07-14) -++++++++++++++++++ - -- Add Table.autofit -- Add feature #46: _Cell.width - - -0.7.2 (2014-07-13) -++++++++++++++++++ - -- Fix: Word does not interpret as line feed - - -0.7.1 (2014-07-11) -++++++++++++++++++ - -- Add feature #14: Run.add_picture() - - -0.7.0 (2014-06-27) -++++++++++++++++++ - -- Add feature #68: Paragraph.insert_paragraph_before() -- Add feature #51: Paragraph.alignment (read/write) -- Add feature #61: Paragraph.text setter -- Add feature #58: Run.add_tab() -- Add feature #70: Run.clear() -- Add feature #60: Run.text setter -- Add feature #39: Run.text and Paragraph.text interpret '\n' and '\t' chars - - -0.6.0 (2014-06-22) -++++++++++++++++++ - -- Add feature #15: section page size -- Add feature #66: add section -- Add page margins and page orientation properties on Section -- Major refactoring of oxml layer - - -0.5.3 (2014-05-10) -++++++++++++++++++ - -- Add feature #19: Run.underline property - - -0.5.2 (2014-05-06) -++++++++++++++++++ - -- Add feature #17: character style - - -0.5.1 (2014-04-02) -++++++++++++++++++ - -- Fix issue #23, `Document.add_picture()` raises ValueError when document - contains VML drawing. - - -0.5.0 (2014-03-02) -++++++++++++++++++ - -- Add 20 tri-state properties on Run, including all-caps, double-strike, - hidden, shadow, small-caps, and 15 others. - - -0.4.0 (2014-03-01) -++++++++++++++++++ - -- Advance from alpha to beta status. -- Add pure-python image header parsing; drop Pillow dependency - - -0.3.0a5 (2014-01-10) -++++++++++++++++++++++ - -- Hotfix: issue #4, Document.add_picture() fails on second and subsequent - images. - - -0.3.0a4 (2014-01-07) -++++++++++++++++++++++ - -- Complete Python 3 support, tested on Python 3.3 - - -0.3.0a3 (2014-01-06) -++++++++++++++++++++++ - -- Fix setup.py error on some Windows installs - - -0.3.0a1 (2014-01-05) -++++++++++++++++++++++ - -- Full object-oriented rewrite -- Feature-parity with prior version -- text: add paragraph, run, text, bold, italic -- table: add table, add row, add column -- styles: specify style for paragraph, table -- picture: add inline picture, auto-scaling -- breaks: add page break -- tests: full pytest and behave-based 2-layer test suite - - -0.3.0dev1 (2013-12-14) -++++++++++++++++++++++ - -- Round-trip .docx file, preserving all parts and relationships -- Load default "template" .docx on open with no filename -- Open from stream and save to stream (file-like object) -- Add paragraph at and of document diff --git a/README.rst b/README.rst index 9a104f2d8..b86bde56c 100644 --- a/README.rst +++ b/README.rst @@ -1,20 +1,19 @@ -Bayoo-docx -=========== +#Bayoo-docx Python library forked from `python-docx `_. The main purpose of the fork was to add implementation for comments and footnotes to the library -Installation ------------- +##Installation + Use the package manager `pip `_ to install bayoo-docx. `pip install bayoo-docx` -Usage ------ +##Usage + :: import docx @@ -29,7 +28,6 @@ Usage -License --------------- +##License -`MIT `_ \ No newline at end of file +`MIT `_ From 6bdb559d338f9a43e9f4dead3ec4f824988a299b Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 18:49:42 +0300 Subject: [PATCH 45/70] add comments implementation on a run level --- docx/__init__.py | 2 +- docx/oxml/text/run.py | 18 ++++++++++++++++++ docx/text/run.py | 8 ++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 1634b762f..410e3481d 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.6' +__version__ = '0.2.8' # register custom Part classes with opc package reader diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index e93575e52..489b28c86 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -58,6 +58,24 @@ def add_drawing(self, inline_or_anchor): drawing.append(inline_or_anchor) return drawing + def add_comm(self, author, comment_part, initials, dtime, comment_text): + + comment = comment_part.add_comment(author, initials, dtime) + comment._add_p(comment_text) + # _r = self.add_r() + self.add_comment_reference(comment._id) + self.link_comment(comment._id) + + return comment + + def link_comment(self, _id): + rStart = OxmlElement('w:commentRangeStart') + rStart._id = _id + rEnd = OxmlElement('w:commentRangeEnd') + rEnd._id = _id + self.insert(0,rStart) + self.append(rEnd) + def add_comment_reference(self, _id): reference = OxmlElement('w:commentReference') reference._id = _id diff --git a/docx/text/run.py b/docx/text/run.py index 360ad83cb..ffcc10199 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import, print_function, unicode_literals +from datetime import datetime from docx.oxml.ns import qn @@ -82,6 +83,13 @@ def add_text(self, text): t = self._r.add_t(text) return _Text(t) + def add_comment(self, text, author='python-docx', initials='pd', dtime=None): + comment_part = self.part._comments_part.element + if dtime is None: + dtime = str( datetime.now() ).replace(' ', 'T') + comment = self._r.add_comm(author, comment_part, initials, dtime, text) + + return comment @property def bold(self): """ From db56c4c6c38fb2d863fa599ef55db9fe8dbb45e4 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 19:19:52 +0300 Subject: [PATCH 46/70] update readme --- HISTORY.rst | 6 +++--- README.rst | 23 ++++++++++++++++------- setup.py | 7 +++++-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 2ef32df8f..d94b29456 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,13 +4,13 @@ #Release History BayooG/bayoo-docx forked from (python-openxmm/python-docx) -###0.2.7 (2020-05-01) - +0.2.8 (2020-05-02) +- add comments implementation on a run level - fix issue with comments date (comments dates are set to current date) -###0.2.4 (2019-9-4) +0.2.4 (2019-9-4) - loop over all the document chieldern (Paragraphs, Tables, Sections) with the right order `document.elements` - addons to Paragraph Object (delete, heading_level, merge_paragraph ) diff --git a/README.rst b/README.rst index b86bde56c..612066913 100644 --- a/README.rst +++ b/README.rst @@ -1,18 +1,20 @@ -#Bayoo-docx +Bayoo-docx +========== Python library forked from `python-docx `_. The main purpose of the fork was to add implementation for comments and footnotes to the library -##Installation - +Installation +------------ Use the package manager `pip `_ to install bayoo-docx. `pip install bayoo-docx` -##Usage +Usage +----- :: @@ -20,14 +22,21 @@ Use the package manager `pip `_ to install bayoo-d document = docx.Document() - paragraph = document.add_paragraph('text') # create new paragraph + paragraph1 = document.add_paragraph('text') # create new paragraph + + comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # add a comment on the entire paragraph + + paragraph2 = document.add_paragraph('text') # create another paragraph + + run = paragraph2.add_run('texty') add a run to the paragraph - comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # create a comment' + run.add_comment('comment') # add a comment only for the run text paragraph.add_footnote('footnote text') # add a footnote -##License +License +------- `MIT `_ diff --git a/setup.py b/setup.py index 091c4342a..dc4f9f399 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from setuptools import find_packages, setup -def text_of(relpath): +def text_of(relpath, long_description=False): """ Return string containing the contents of the file at *relpath* relative to this file. @@ -15,6 +15,9 @@ def text_of(relpath): file_path = os.path.join(thisdir, os.path.normpath(relpath)) with open(file_path) as f: text = f.read() + if long_description: + text = re.sub(text, '=+\n', '\n') + text = re.sub(text, '-+\n', '\n') return text @@ -58,7 +61,7 @@ def text_of(relpath): 'Topic :: Software Development :: Libraries' ] -LONG_DESCRIPTION = text_of('README.rst') + '\n\n' + text_of('HISTORY.rst') +LONG_DESCRIPTION = text_of('README.rst',long_description=True) + '\n\n' + text_of('HISTORY.rst') params = { From d3870bcb6ec3a23b032baacf176224af454e0816 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 23:17:33 +0300 Subject: [PATCH 47/70] PYPI DESCRIPTION --- DESCRIPTION.rst | 42 ++++++++++++++++++++++++++++++++++++++++++ docx/__init__.py | 2 +- setup.py | 7 ++----- 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 DESCRIPTION.rst diff --git a/DESCRIPTION.rst b/DESCRIPTION.rst new file mode 100644 index 000000000..f52196ec2 --- /dev/null +++ b/DESCRIPTION.rst @@ -0,0 +1,42 @@ +Bayoo-docx + + +Python library forked from `python-docx `_. + +The main purpose of the fork was to add implementation for comments and footnotes to the library + +Installation + + +Use the package manager `pip `_ to install bayoo-docx. + + +`pip install bayoo-docx` + +Usage + + +:: + + import docx + + document = docx.Document() + + paragraph1 = document.add_paragraph('text') # create new paragraph + + comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # add a comment on the entire paragraph + + paragraph2 = document.add_paragraph('text') # create another paragraph + + run = paragraph2.add_run('texty') add a run to the paragraph + + run.add_comment('comment') # add a comment only for the run text + + paragraph.add_footnote('footnote text') # add a footnote + + + +License + + +`MIT `_ diff --git a/docx/__init__.py b/docx/__init__.py index 410e3481d..d2b7b6930 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.8' +__version__ = '0.2.9' # register custom Part classes with opc package reader diff --git a/setup.py b/setup.py index dc4f9f399..ff8652647 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from setuptools import find_packages, setup -def text_of(relpath, long_description=False): +def text_of(relpath): """ Return string containing the contents of the file at *relpath* relative to this file. @@ -15,9 +15,6 @@ def text_of(relpath, long_description=False): file_path = os.path.join(thisdir, os.path.normpath(relpath)) with open(file_path) as f: text = f.read() - if long_description: - text = re.sub(text, '=+\n', '\n') - text = re.sub(text, '-+\n', '\n') return text @@ -61,7 +58,7 @@ def text_of(relpath, long_description=False): 'Topic :: Software Development :: Libraries' ] -LONG_DESCRIPTION = text_of('README.rst',long_description=True) + '\n\n' + text_of('HISTORY.rst') +LONG_DESCRIPTION = text_of('DESCRIPTION.rst') + '\n\n' + text_of('HISTORY.rst') params = { From c5ecc0b85e5df20d90bdc1072dcfbb205fdf18b2 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 2 May 2020 23:19:14 +0300 Subject: [PATCH 48/70] . --- test.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 test.py diff --git a/test.py b/test.py deleted file mode 100644 index 5cdf8a401..000000000 --- a/test.py +++ /dev/null @@ -1,13 +0,0 @@ -import docx - -print(docx.__version__) - -d= docx.Document() - -p = d.add_paragraph('r1') - -r = p.add_run('r2') - -r.add_comment('hola comm') - -d.save('d.docx') \ No newline at end of file From a4dd0f6b7caf7702e5eb63aa84e4f7ae511c942c Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 21 May 2020 02:01:04 +0300 Subject: [PATCH 49/70] pypi publish workflow --- .github/workflows/pythonpublish.yml | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/pythonpublish.yml diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml new file mode 100644 index 000000000..4e1ef42d2 --- /dev/null +++ b/.github/workflows/pythonpublish.yml @@ -0,0 +1,31 @@ +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* From 2941f32ce530d831758553fa803a19d72062ff47 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 21 May 2020 02:20:08 +0300 Subject: [PATCH 50/70] . --- .github/workflows/pythonpublish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 4e1ef42d2..265751242 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -4,8 +4,8 @@ name: Upload Python Package on: - release: - types: [created] + pull_request: + branches: master jobs: deploy: From 1ab515deb5d3df7186b11eada819152cc2518168 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 21 May 2020 02:23:34 +0300 Subject: [PATCH 51/70] . --- .github/workflows/pythonpublish.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 265751242..8027a2089 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -5,7 +5,9 @@ name: Upload Python Package on: pull_request: - branches: master + branches: + - master + - develop jobs: deploy: From ce19d5561df7258b04714326bf8f8671e16ee7d1 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 21 May 2020 02:32:00 +0300 Subject: [PATCH 52/70] update __version __ --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index d2b7b6930..5c5c6dbcd 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.9' +__version__ = '0.2.9-b' # register custom Part classes with opc package reader From e982e543b7de38de4e992174084b09c8cebdf503 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Thu, 21 May 2020 02:48:07 +0300 Subject: [PATCH 53/70] update the action --- .github/workflows/pythonpublish.yml | 8 +++----- docx/__init__.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 8027a2089..521fffae6 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -1,13 +1,11 @@ # This workflows will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries -name: Upload Python Package +name: Publish on PYPI on: - pull_request: - branches: - - master - - develop + release: + types: [created] jobs: deploy: diff --git a/docx/__init__.py b/docx/__init__.py index 5c5c6dbcd..6a26c4789 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.9-b' +__version__ = '0.2.10' # register custom Part classes with opc package reader From 6048db58d6968ae4d66f80e6021529b71698cd20 Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Mon, 17 Aug 2020 14:04:40 +0300 Subject: [PATCH 54/70] pass comment part in add comment --- docx/text/paragraph.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 38d2a4e39..019d6641f 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -48,8 +48,9 @@ def delete(self): self._p.getparent().remove(self._p) self._p = self._element = None - def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0): - comment_part = self.part._comments_part.element + def add_comment(self, text, author='python-docx', initials='pd', dtime=None ,rangeStart=0, rangeEnd=0, comment_part=None): + if comment_part is None: + comment_part = self.part._comments_part.element if dtime is None: dtime = str( datetime.now() ).replace(' ', 'T') comment = self._p.add_comm(author, comment_part, initials, dtime, text, rangeStart, rangeEnd) From 32400b2ac831f5f5458057a8b7a0c8c5c9ce2dbc Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Mon, 17 Aug 2020 14:06:05 +0300 Subject: [PATCH 55/70] update version --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 6a26c4789..ad408aa84 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.10' +__version__ = '0.2.11' # register custom Part classes with opc package reader From 2ef9cba72d8c88d3710051a9e7834dc5b208904d Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 23 Oct 2020 21:41:25 +0300 Subject: [PATCH 56/70] update readme --- README.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.rst b/README.rst index 612066913..c8a0d6035 100644 --- a/README.rst +++ b/README.rst @@ -32,9 +32,17 @@ Usage run.add_comment('comment') # add a comment only for the run text + run.add_comment('comment2') + + run_comments = run.comments + paragraph.add_footnote('footnote text') # add a footnote +Donation +------------ +`bitcoin: bc1q9dftn4ndufwzyzkm8ryu0kky0v8y7w00zdah9r` + License ------- From dbca07f5cb5c07f15437a61dbc76b8184e99dc0b Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 23 Oct 2020 21:41:37 +0300 Subject: [PATCH 57/70] update version --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index ad408aa84..f0803cea9 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.11' +__version__ = '0.2.12' # register custom Part classes with opc package reader From c634b2c563ec8ee8520143409961298bde068b8d Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 23 Oct 2020 21:43:40 +0300 Subject: [PATCH 58/70] list and edit comments --- docx/oxml/comments.py | 2 -- docx/text/comment.py | 23 +++++++++++++++++++++++ docx/text/paragraph.py | 5 +++++ docx/text/run.py | 11 ++++++++++- 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 docx/text/comment.py diff --git a/docx/oxml/comments.py b/docx/oxml/comments.py index 49afde5ce..214a88f20 100644 --- a/docx/oxml/comments.py +++ b/docx/oxml/comments.py @@ -52,8 +52,6 @@ def paragraph(self): return Paragraph(self.p, self) - - class CT_Comments(BaseOxmlElement): """ A ```` element, a container for Comments properties diff --git a/docx/text/comment.py b/docx/text/comment.py new file mode 100644 index 000000000..adc0110a1 --- /dev/null +++ b/docx/text/comment.py @@ -0,0 +1,23 @@ +from ..shared import Parented + +class Comment(Parented): + """[summary] + + :param Parented: [description] + :type Parented: [type] + """ + def __init__(self, com, parent): + super(Comment, self).__init__(parent) + self._com = self._element = self.element = com + + @property + def paragraph(self): + return self.element.paragraph + + @property + def text(self): + return self.element.paragraph.text + + @text.setter + def text(self, text): + self.element.paragraph.text = text \ No newline at end of file diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index 019d6641f..ac0fe674b 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -225,6 +225,11 @@ def footnotes(self): else : return False + @property + def comments(self): + runs_comments = [run.comments for run in self.runs] + return [comment for comments in runs_comments for comment in comments] + @text.setter def text(self, text): self.clear() diff --git a/docx/text/run.py b/docx/text/run.py index ffcc10199..77f25e45b 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -15,6 +15,7 @@ from ..shape import InlineShape from ..shared import Parented +from .comment import Comment class Run(Parented): """ @@ -230,7 +231,15 @@ def get_hyperLink(self): return '', False else: return 'None' - + + @property + def comments(self): + comment_part = self._parent._parent.part._comments_part.element + comment_refs = self._element.findall(qn('w:commentReference')) + ids = [int(ref.get(qn('w:id'))) for ref in comment_refs] + coms = [com for com in comment_part if com._id in ids] + return [Comment(com, comment_part) for com in coms] + class _Text(object): """ From 0d4ba9a8255c5ea3e97b83af95c1e5d6291b02cb Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Fri, 23 Oct 2020 21:48:19 +0300 Subject: [PATCH 59/70] update readme --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c8a0d6035..f706025cd 100644 --- a/README.rst +++ b/README.rst @@ -41,7 +41,9 @@ Usage Donation ------------ -`bitcoin: bc1q9dftn4ndufwzyzkm8ryu0kky0v8y7w00zdah9r` +:: + + bitcoin: bc1q9dftn4ndufwzyzkm8ryu0kky0v8y7w00zdah9r License From 7082f5dd2dc12d29f09bb99ac4e7a6da0d921013 Mon Sep 17 00:00:00 2001 From: BayooG Date: Tue, 3 Nov 2020 15:07:34 +0200 Subject: [PATCH 60/70] all runs atrib --- docx/__init__.py | 2 +- docx/text/paragraph.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docx/__init__.py b/docx/__init__.py index f0803cea9..8ddf3ff82 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.12' +__version__ = '0.2.13' # register custom Part classes with opc package reader diff --git a/docx/text/paragraph.py b/docx/text/paragraph.py index ac0fe674b..9bf676b92 100644 --- a/docx/text/paragraph.py +++ b/docx/text/paragraph.py @@ -127,6 +127,9 @@ def runs(self): return [Run(r, self) for r in self._p.r_lst] @property + def all_runs(self): + return [Run(r, self) for r in self._p.xpath('.//w:r[not(ancestor::w:r)]')] + @property def style(self): """ Read/Write. |_ParagraphStyle| object representing the style assigned @@ -215,8 +218,7 @@ def is_heading(self): @property def full_text(self): - allRuns = [Run(r, self) for r in self._p.xpath('.//w:r[not(ancestor::w:r)]')] - return u"".join([r.text for r in allRuns]) + return u"".join([r.text for r in self.all_runs]) @property def footnotes(self): From 6ad55330e1c1fbe2e88f899319cd1a789b126805 Mon Sep 17 00:00:00 2001 From: Albert Kisiel Date: Tue, 17 Nov 2020 15:30:37 +0100 Subject: [PATCH 61/70] prepend and append comment elements --- docx/oxml/text/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index 489b28c86..8f3a9c70f 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -73,8 +73,8 @@ def link_comment(self, _id): rStart._id = _id rEnd = OxmlElement('w:commentRangeEnd') rEnd._id = _id - self.insert(0,rStart) - self.append(rEnd) + self.addprevious(rStart) + self.addnext(rEnd) def add_comment_reference(self, _id): reference = OxmlElement('w:commentReference') From 94c715232d068e252a1a507540f00a52a73be360 Mon Sep 17 00:00:00 2001 From: BayooG Date: Thu, 19 Nov 2020 08:44:08 +0200 Subject: [PATCH 62/70] update version --- CONTRIBUTORS.md | 8 +++++--- docx/__init__.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 621b58882..ac9440518 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,10 +1,12 @@ -Bayoo-docx contributors (sorted alphabetically) +## Bayoo-docx contributors ============================================ -* **[Bassel Al Madani](https://github.com/pepos9)** - * **[Obay Daba](https://github.com/bayoog)** +* **[Bassel Al Madani](https://github.com/pepos9)** + * **[Tareq Ibrahim](https://github.com/idtareq)** +* **[baltazarix](https://github.com/baltazarix)** + diff --git a/docx/__init__.py b/docx/__init__.py index 8ddf3ff82..79ff81dac 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.13' +__version__ = '0.2.14' # register custom Part classes with opc package reader From 9be99b30865fe0be1c579acbcb372fc9c1d52b5b Mon Sep 17 00:00:00 2001 From: Ahmad Alwareh <55667622+ahmadalwareh@users.noreply.github.com> Date: Mon, 31 Oct 2022 22:10:20 +0200 Subject: [PATCH 63/70] Create add_ole_object_to_run func (#13) * Update version * reverse version to origin * Add add_ole_object_to_run func --- docx/text/run.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docx/text/run.py b/docx/text/run.py index 77f25e45b..89355999d 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -8,6 +8,7 @@ from datetime import datetime from docx.oxml.ns import qn +from docx.opc.part import PackURI, Part from ..enum.style import WD_STYLE_TYPE from ..enum.text import WD_BREAK @@ -239,7 +240,25 @@ def comments(self): ids = [int(ref.get(qn('w:id'))) for ref in comment_refs] coms = [com for com in comment_part if com._id in ids] return [Comment(com, comment_part) for com in coms] - + + + def add_ole_object_to_run(self, ole_object_path): + """ + Add saved OLE Object in the disk to an run and retun the newly created relationship ID + Note: OLE Objects must be stored in the disc as `.bin` file + """ + reltype: str = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject" + pack_path: str = "/word/embeddings/" + ole_object_path.split("\\")[-1] + partname = PackURI(pack_path) + content_type: str = "application/vnd.openxmlformats-officedocument.oleObject" + + with open(ole_object_path, "rb") as f: + blob = f.read() + target_part = Part(partname=partname, content_type=content_type, blob=blob) + rel_id: str = self.part.rels._next_rId + self.part.rels.add_relationship(reltype=reltype, target=target_part, rId=rel_id) + return rel_id + class _Text(object): """ From 87a29931cd91c5bc9124b15e1c137e47d8ec199f Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Mon, 31 Oct 2022 21:19:56 +0100 Subject: [PATCH 64/70] release version 0.2.15 (#14) --- CONTRIBUTORS.md | 2 ++ docx/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ac9440518..8e79b0ab8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,4 +9,6 @@ * **[Tareq Ibrahim](https://github.com/idtareq)** * **[baltazarix](https://github.com/baltazarix)** + +* **[Ahmad Alwareh](https://github.com/ahmadalwareh)** diff --git a/docx/__init__.py b/docx/__init__.py index 79ff81dac..df47882c3 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.14' +__version__ = '0.2.15' # register custom Part classes with opc package reader From c7a413a0b9b29e3041ad3b3a44298196505447d6 Mon Sep 17 00:00:00 2001 From: bayoog Date: Mon, 31 Oct 2022 21:22:22 +0100 Subject: [PATCH 65/70] update version --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index df47882c3..68d8c43cd 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.15' +__version__ = '0.2.18' # register custom Part classes with opc package reader From 62bfcde8b8a104f37b4199c0e903586e069ae7a3 Mon Sep 17 00:00:00 2001 From: Ahmad Alwareh <55667622+ahmadalwareh@users.noreply.github.com> Date: Tue, 1 Nov 2022 12:16:17 +0200 Subject: [PATCH 66/70] Bugfix and Update README (#15) * Update version * reverse version to origin * Add add_ole_object_to_run func * Update README * Update README * Update README * Update README --- README.rst => README.md | 15 +++++++-------- docx/text/run.py | 3 ++- 2 files changed, 9 insertions(+), 9 deletions(-) rename README.rst => README.md (66%) diff --git a/README.rst b/README.md similarity index 66% rename from README.rst rename to README.md index f706025cd..56621603f 100644 --- a/README.rst +++ b/README.md @@ -1,34 +1,34 @@ Bayoo-docx ========== -Python library forked from `python-docx `_. +Python library forked from [python-docx](https://github.com/python-openxml/python-docx). The main purpose of the fork was to add implementation for comments and footnotes to the library Installation ------------ -Use the package manager `pip `_ to install bayoo-docx. +Use the package manager [pip](https://pypi.org/project/bayoo-docx/) to install bayoo-docx. `pip install bayoo-docx` -Usage +Usage: ----- -:: + import docx document = docx.Document() - paragraph1 = document.add_paragraph('text') # create new paragraph + paragraph = document.add_paragraph('text') # create new paragraph comment = paragraph.add_comment('comment',author='Obay Daba',initials= 'od') # add a comment on the entire paragraph paragraph2 = document.add_paragraph('text') # create another paragraph - run = paragraph2.add_run('texty') add a run to the paragraph + run = paragraph2.add_run('text1') #add a run to the paragraph run.add_comment('comment') # add a comment only for the run text @@ -41,7 +41,6 @@ Usage Donation ------------ -:: bitcoin: bc1q9dftn4ndufwzyzkm8ryu0kky0v8y7w00zdah9r @@ -49,4 +48,4 @@ Donation License ------- -`MIT `_ +[MIT](https://choosealicense.com/licenses/mit/) diff --git a/docx/text/run.py b/docx/text/run.py index 89355999d..79185d3f4 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -8,7 +8,8 @@ from datetime import datetime from docx.oxml.ns import qn -from docx.opc.part import PackURI, Part +from docx.opc.part import * + from ..enum.style import WD_STYLE_TYPE from ..enum.text import WD_BREAK From 5e1be2848b877ad6fe0950c82cb2831aab8e632e Mon Sep 17 00:00:00 2001 From: bayoog Date: Tue, 1 Nov 2022 11:51:50 +0100 Subject: [PATCH 67/70] update version --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 68d8c43cd..1d3aaac95 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.18' +__version__ = '0.2.19' # register custom Part classes with opc package reader From 66528f6ac06d96a210b0cb91a27a8429fd29f5a4 Mon Sep 17 00:00:00 2001 From: Ahmad Alwareh <55667622+ahmadalwareh@users.noreply.github.com> Date: Sun, 26 Feb 2023 00:03:52 +0200 Subject: [PATCH 68/70] feat: support `fldChar` and `instrText` elements (#17) * Update version * reverse version to origin * Add add_ole_object_to_run func * Update README * Update README * Update README * Update README * Add fldChar element support * Add instrText element support * Set instr_text element as a property * Set instr_text element delete * Avoid duplication if add instrtext element with run already has --- docx/oxml/text/run.py | 63 ++++++++++++++++++++++++++++++++----------- docx/text/run.py | 41 +++++++++++++++++++++------- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/docx/oxml/text/run.py b/docx/oxml/text/run.py index 8f3a9c70f..255c45a35 100644 --- a/docx/oxml/text/run.py +++ b/docx/oxml/text/run.py @@ -9,7 +9,7 @@ from .. import OxmlElement from ..xmlchemy import ( - BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne ,RequiredAttribute + BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne, RequiredAttribute ) from .. import OxmlElement @@ -28,7 +28,7 @@ class CT_R(BaseOxmlElement): ```` element, containing the properties and text for a run. """ rPr = ZeroOrOne('w:rPr') - ###wrong + # wrong ref = ZeroOrOne('w:commentRangeStart', successors=('w:r',)) t = ZeroOrMore('w:t') br = ZeroOrMore('w:br') @@ -59,7 +59,7 @@ def add_drawing(self, inline_or_anchor): return drawing def add_comm(self, author, comment_part, initials, dtime, comment_text): - + comment = comment_part.add_comment(author, initials, dtime) comment._add_p(comment_text) # _r = self.add_r() @@ -67,7 +67,7 @@ def add_comm(self, author, comment_part, initials, dtime, comment_text): self.link_comment(comment._id) return comment - + def link_comment(self, _id): rStart = OxmlElement('w:commentRangeStart') rStart._id = _id @@ -81,7 +81,7 @@ def add_comment_reference(self, _id): reference._id = _id self.append(reference) return reference - + def add_footnote_reference(self, _id): rPr = self.get_or_add_rPr() rstyle = rPr.get_or_add_rStyle() @@ -90,13 +90,13 @@ def add_footnote_reference(self, _id): reference._id = _id self.append(reference) return reference - + def add_footnoteRef(self): ref = OxmlElement('w:footnoteRef') self.append(ref) return ref - + def footnote_style(self): rPr = self.get_or_add_rPr() rstyle = rPr.get_or_add_rStyle() @@ -104,14 +104,14 @@ def footnote_style(self): self.add_footnoteRef() return self - + @property def footnote_id(self): _id = self.xpath('./w:footnoteReference/@w:id') - if len(_id) > 1 or len(_id) == 0 : + if len(_id) > 1 or len(_id) == 0: return None else: - return int(_id[0]) + return int(_id[0]) def clear_content(self): """ @@ -172,6 +172,39 @@ def text(self, text): self.clear_content() _RunContentAppender.append_to_run_from_text(self, text) + def add_fldChar(self, fldCharType, fldLock=False, dirty=False): + if fldCharType not in ("begin", "end", "separate"): + return None + + fld_char = OxmlElement("w:fldChar") + fld_char.set(qn("w:fldCharType"), fldCharType) + if fldLock: + fld_char.set(qn("w:fldLock"), "true") + elif dirty: + fld_char.set(qn("w:fldLock"), "true") + self.append(fld_char) + return fld_char + + @property + def instr_text(self): + for child in list(self): + if child.tag.endswith("instrText"): + return child + return None + + @instr_text.setter + def instr_text(self, instr_text_val): + if self.instr_text is not None: + self._remove_instr_text() + + instr_text = OxmlElement("w:instrText") + instr_text.text = instr_text_val + self.append(instr_text) + + def _remove_instr_text(self): + for child in self.iterchildren("{*}instrText"): + self.remove(child) + class CT_Text(BaseOxmlElement): """ @@ -180,11 +213,12 @@ class CT_Text(BaseOxmlElement): class CT_RPr(BaseOxmlElement): - rStyle = ZeroOrOne('w:rStyle') - + rStyle = ZeroOrOne('w:rStyle') + class CT_RStyle(BaseOxmlElement): - val = RequiredAttribute('w:val',ST_String) + val = RequiredAttribute('w:val', ST_String) + class _RunContentAppender(object): """ @@ -195,6 +229,7 @@ class _RunContentAppender(object): appended. Likewise a newline or carriage return character ('\n', '\r') causes a ```` element to be appended. """ + def __init__(self, r): self._r = r self._bfr = [] @@ -240,5 +275,3 @@ def flush(self): if text: self._r.add_t(text) del self._bfr[:] - - diff --git a/docx/text/run.py b/docx/text/run.py index 79185d3f4..0ed3e972a 100644 --- a/docx/text/run.py +++ b/docx/text/run.py @@ -10,7 +10,6 @@ from docx.oxml.ns import qn from docx.opc.part import * - from ..enum.style import WD_STYLE_TYPE from ..enum.text import WD_BREAK from .font import Font @@ -19,6 +18,7 @@ from .comment import Comment + class Run(Parented): """ Proxy object wrapping ```` element. Several of the properties on Run @@ -27,6 +27,7 @@ class Run(Parented): not specified directly on the run and its effective value is taken from the style hierarchy. """ + def __init__(self, r, parent): super(Run, self).__init__(parent) self._r = self._element = self.element = r @@ -89,10 +90,11 @@ def add_text(self, text): def add_comment(self, text, author='python-docx', initials='pd', dtime=None): comment_part = self.part._comments_part.element if dtime is None: - dtime = str( datetime.now() ).replace(' ', 'T') - comment = self._r.add_comm(author, comment_part, initials, dtime, text) + dtime = str(datetime.now()).replace(' ', 'T') + comment = self._r.add_comm(author, comment_part, initials, dtime, text) return comment + @property def bold(self): """ @@ -193,11 +195,11 @@ def underline(self): @underline.setter def underline(self, value): self.font.underline = value - + @property def footnote(self): _id = self._r.footnote_id - + if _id is not None: footnotes_part = self._parent._parent.part._footnotes_part.element footnote = footnotes_part.get_footnote_by_id(_id) @@ -217,7 +219,7 @@ def get_hyperLink(self): returns the text of the hyperlink of the run in case of the run has a hyperlink """ document = self._parent._parent.document - parent = self.element.getparent() + parent = self.element.getparent() linkText = '' if self.is_hyperlink: if parent.attrib.__contains__(qn('r:id')): @@ -241,8 +243,7 @@ def comments(self): ids = [int(ref.get(qn('w:id'))) for ref in comment_refs] coms = [com for com in comment_part if com._id in ids] return [Comment(com, comment_part) for com in coms] - - + def add_ole_object_to_run(self, ole_object_path): """ Add saved OLE Object in the disk to an run and retun the newly created relationship ID @@ -252,19 +253,39 @@ def add_ole_object_to_run(self, ole_object_path): pack_path: str = "/word/embeddings/" + ole_object_path.split("\\")[-1] partname = PackURI(pack_path) content_type: str = "application/vnd.openxmlformats-officedocument.oleObject" - + with open(ole_object_path, "rb") as f: blob = f.read() target_part = Part(partname=partname, content_type=content_type, blob=blob) rel_id: str = self.part.rels._next_rId self.part.rels.add_relationship(reltype=reltype, target=target_part, rId=rel_id) return rel_id - + + def add_fldChar(self, fldCharType, fldLock: bool = False, dirty: bool = False): + + fldChar = self._r.add_fldChar(fldCharType, fldLock, dirty) + return fldChar + + @property + def instr_text(self): + return self._r.instr_text + + @instr_text.setter + def instr_text(self, instr_text_val): + self._r.instr_text = instr_text_val + + def remove_instr_text(self): + if self.instr_text is None: + return None + else: + self._r._remove_instr_text() + class _Text(object): """ Proxy object wrapping ```` element. """ + def __init__(self, t_elm): super(_Text, self).__init__() self._t = t_elm From 60ca0634c767dfe48fc010b97a53e8de6fabfabd Mon Sep 17 00:00:00 2001 From: Obay Daba Date: Sat, 25 Feb 2023 23:05:55 +0100 Subject: [PATCH 69/70] chore: bump version to `0.2.20` --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 1d3aaac95..ee8a52ad1 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = '0.2.19' +__version__ = '0.2.20' # register custom Part classes with opc package reader From a998646844407c6de88ce8a1bbfba0195cdaf9ad Mon Sep 17 00:00:00 2001 From: dj-obyte Date: Thu, 20 Jun 2024 11:49:08 +0200 Subject: [PATCH 70/70] Update README.md (#20) --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 56621603f..74fb0842e 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,3 @@ Usage: paragraph.add_footnote('footnote text') # add a footnote -Donation ------------- - - bitcoin: bc1q9dftn4ndufwzyzkm8ryu0kky0v8y7w00zdah9r - - -License -------- - -[MIT](https://choosealicense.com/licenses/mit/)