From 436e8a2f54b23860fdc029769085e787bc0b985d Mon Sep 17 00:00:00 2001 From: yunshi Date: Mon, 14 Mar 2016 21:10:40 +0100 Subject: [PATCH 01/14] docs: document hyperlink analysis --- docs/dev/analysis/features/text/hyperlink.rst | 301 ++++++++++++++++++ docs/dev/analysis/features/text/index.rst | 3 + 2 files changed, 304 insertions(+) create mode 100644 docs/dev/analysis/features/text/hyperlink.rst diff --git a/docs/dev/analysis/features/text/hyperlink.rst b/docs/dev/analysis/features/text/hyperlink.rst new file mode 100644 index 000000000..aa8788da3 --- /dev/null +++ b/docs/dev/analysis/features/text/hyperlink.rst @@ -0,0 +1,301 @@ + +Hyperlink +========= + +Word allows hyperlinks to be placed in a document. + +The target of a hyperlink may be external, such as a web site, or internal, +to another location in the document. + +A hyperlink can contain multiple runs of text, each with its own distinct +text formatting (font). + + +Candidate protocol +------------------ + +An external hyperlink has an address and an optional anchor. An internal +hyperlink has only an anchor. + +.. highlight:: python + +**Add the external hyperlink** `http://us.com#about`:: + + >>> hyperlink = paragraph.add_hyperlink('About', address='http://us.com', anchor='about') + >>> hyperlink + + >>> hyperlink.text + 'About' + >>> hyperlink.address + 'http://us.com' + >>> hyperlink.anchor + 'about' + +**Add an internal hyperlink (to a bookmark)**:: + + >>> hyperlink = paragraph.add_hyperlink('Section 1', anchor='Section_1') + >>> hyperlink.text + 'Section 1' + >>> hyperlink.anchor + 'Section_1' + >>> hyperlink.address + None + +**Modify hyperlink properties**:: + + >>> hyperlink.text = 'Froogle' + >>> hyperlink.text + 'Froogle' + >>> hyperlink.address = 'mailto:info@froogle.com?subject=sup dawg?' + >>> hyperlink.address + 'mailto:info@froogle.com?subject=sup%20dawg%3F' + >>> hyperlink.anchor = None + >>> hyperlink.anchor + None + +**Add additional runs to a hyperlink**:: + + >>> hyperlink.text = 'A ' + >>> # .insert_run inserts a new run at idx, defaults to idx=-1 + >>> hyperlink.insert_run(' link').bold = True + >>> hyperlink.insert_run('formatted', idx=1).bold = True + >>> hyperlink.text + 'A formatted link' + >>> [r for r in hyperlink.iter_runs()] + [, + , + ] + +**Iterate over the run-level items a paragraph contains**:: + + >>> paragraph = document.add_paragraph('A paragraph having a link to: ') + >>> paragraph.add_hyperlink(text='github', address='http://github.com') + >>> [item for item in paragraph.iter_run_level_items()]: + [, ] + +**Paragraph.text now includes text contained in a hyperlink**:: + + >>> paragraph.text + 'A paragraph having a link to: github' + + +Word Behaviors +-------------- + +* What are the semantics of the w:history attribute on w:hyperlink? I'm + suspecting this indicates whether the link should show up blue (unvisited) + or purple (visited). I'm inclined to think we need that as a read/write + property on hyperlink. We should see what the MS API does on this count. + +* We probably need to enforce some character-set restrictions on w:anchor. + Word doesn't seem to like spaces or hyphens, for example. The simple type + ST_String doesn't look like it takes care of this. + +* We'll need to test URL escaping of special characters like spaces and + question marks in Hyperlink.address. + +* What does Word do when loading a document containing an internal hyperlink + having an anchor value that doesn't match an existing bookmark? We'll want + to know because we're sure to get support inquiries from folks who don't + match those up and wonder why they get a repair error or whatever. + + +Specimen XML +------------ + +.. highlight:: xml + + +External links +~~~~~~~~~~~~~~ + +The address (URL) of an external hyperlink is stored in the document.xml.rels +file, keyed by the w:hyperlink@r:id attribute:: + + + + This is an external link to + + + + + + + Google + + + + +... mapping to relationship in document.xml.rels:: + + + + + +A hyperlink can contain multiple runs of text (and a whole lot of other +stuff, including nested hyperlinks, at least as far as the schema indicates):: + + + + + + + + A hyperlink containing an + + + + + + + italicized + + + + + + word + + + + + +Internal links +~~~~~~~~~~~~~~ + +An internal link provides "jump to another document location" behavior in the +Word UI. An internal link is distinguished by the absence of an r:id +attribute. In this case, the w:anchor attribute is required. The value of the +anchor attribute is the name of a bookmark in the document. + +Example:: + + + + See + + + + + + + Section 4 + + + + for more details. + + + +... referring to this bookmark elsewhere in the document:: + + + + + Section 4 + + + + + +Schema excerpt +-------------- + +.. highlight:: xml + +:: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/dev/analysis/features/text/index.rst b/docs/dev/analysis/features/text/index.rst index 3b7694731..a9cb92bf8 100644 --- a/docs/dev/analysis/features/text/index.rst +++ b/docs/dev/analysis/features/text/index.rst @@ -5,6 +5,7 @@ Text .. toctree:: :titlesonly: + hyperlink font-highlight-color paragraph-format font @@ -12,3 +13,5 @@ Text underline run-content breaks + hyperlink + From a71fc8b2a4ba35b9d8a6ad26a0d3f48a255ea2d6 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Thu, 30 Sep 2021 21:08:15 -0300 Subject: [PATCH 02/14] Create python-package-conda.yml --- .github/workflows/python-package-conda.yml | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/python-package-conda.yml diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml new file mode 100644 index 000000000..7bae7e247 --- /dev/null +++ b/.github/workflows/python-package-conda.yml @@ -0,0 +1,34 @@ +name: Python Package using Conda + +on: [push] + +jobs: + build-linux: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file environment.yml --name base + - name: Lint with flake8 + run: | + conda install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + conda install pytest + pytest From 276d960e855e9b63993ed606a49458d60ac65858 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Thu, 30 Sep 2021 21:17:16 -0300 Subject: [PATCH 03/14] Create python-package.yml --- .github/workflows/python-package.yml | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 000000000..3bc1e336e --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,43 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Build + run: | + python setup.py + - name: Test with pytest + run: | + pytest From 7266ad5ab9d1c6d3df8483323399a2907e512c21 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Thu, 30 Sep 2021 21:17:34 -0300 Subject: [PATCH 04/14] Delete python-package-conda.yml --- .github/workflows/python-package-conda.yml | 34 ---------------------- 1 file changed, 34 deletions(-) delete mode 100644 .github/workflows/python-package-conda.yml diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml deleted file mode 100644 index 7bae7e247..000000000 --- a/.github/workflows/python-package-conda.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Python Package using Conda - -on: [push] - -jobs: - build-linux: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Add conda to system path - run: | - # $CONDA is an environment variable pointing to the root of the miniconda directory - echo $CONDA/bin >> $GITHUB_PATH - - name: Install dependencies - run: | - conda env update --file environment.yml --name base - - name: Lint with flake8 - run: | - conda install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - conda install pytest - pytest From f7037e169d268cea4474aa52acc4054ed8065c23 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Thu, 30 Sep 2021 21:23:46 -0300 Subject: [PATCH 05/14] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7c34edcca..4e42d08e3 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ def text_of(relpath): KEYWORDS = "docx office openxml word" AUTHOR = "Steve Canny" AUTHOR_EMAIL = "python-docx@googlegroups.com" -URL = "https://github.com/python-openxml/python-docx" +URL = "https://github.com/phgrigorio/python-docx" LICENSE = text_of("LICENSE") PACKAGES = find_packages(exclude=["tests", "tests.*"]) PACKAGE_DATA = {"docx": ["templates/*.xml", "templates/*.docx"]} From 1984cf83534d26c69d6ec0880a508571636f0461 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Thu, 30 Sep 2021 21:37:04 -0300 Subject: [PATCH 06/14] Update __init__.py --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 59756c021..625f6dcf7 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = "0.8.11" +__version__ = "0.8.11.1" # register custom Part classes with opc package reader From ea38c1bf62d496d29d817f78df8441bebb29d9a1 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 07:19:31 -0300 Subject: [PATCH 07/14] Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Teste Update .gitignore --- .gitignore | 1 + docx/__init__.py | 2 +- docx/oxml/__init__.py | 11 ++++ docx/oxml/text/parfmt.py | 128 ++++++++++++++++++++++++++++++++++++++- docx/text/parfmt.py | 28 +++++++++ 5 files changed, 168 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e24445137..030b86cd0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ _scratch/ Session.vim /.tox/ +/.idea diff --git a/docx/__init__.py b/docx/__init__.py index 625f6dcf7..1183fcee0 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = "0.8.11.1" +__version__ = "0.8.11.2" # register custom Part classes with opc package reader diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 093c1b45b..abc26e3f4 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -226,6 +226,11 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): CT_Ind, CT_Jc, CT_PPr, + CT_Border, + CT_BorderTop, + CT_BorderLeft, + CT_BorderBottom, + CT_BorderRight, CT_Spacing, CT_TabStop, CT_TabStops, @@ -236,6 +241,12 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): 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:pBdr', CT_Border) +register_element_cls('w:top', CT_BorderTop) +register_element_cls('w:left', CT_BorderLeft) +register_element_cls('w:bottom', CT_BorderBottom) +register_element_cls('w:right', CT_BorderRight) +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) diff --git a/docx/oxml/text/parfmt.py b/docx/oxml/text/parfmt.py index 466b11b1b..bc0408237 100644 --- a/docx/oxml/text/parfmt.py +++ b/docx/oxml/text/parfmt.py @@ -8,7 +8,7 @@ WD_ALIGN_PARAGRAPH, WD_LINE_SPACING, WD_TAB_ALIGNMENT, WD_TAB_LEADER ) from ...shared import Length -from ..simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure +from ..simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure, ST_String, XsdInt from ..xmlchemy import ( BaseOxmlElement, OneOrMore, OptionalAttribute, RequiredAttribute, ZeroOrOne @@ -53,6 +53,7 @@ class CT_PPr(BaseOxmlElement): pageBreakBefore = ZeroOrOne('w:pageBreakBefore', successors=_tag_seq[4:]) widowControl = ZeroOrOne('w:widowControl', successors=_tag_seq[6:]) numPr = ZeroOrOne('w:numPr', successors=_tag_seq[7:]) + border = ZeroOrOne('w:pBdr', successors=_tag_seq[9:]) tabs = ZeroOrOne('w:tabs', successors=_tag_seq[11:]) spacing = ZeroOrOne('w:spacing', successors=_tag_seq[22:]) ind = ZeroOrOne('w:ind', successors=_tag_seq[23:]) @@ -60,6 +61,23 @@ class CT_PPr(BaseOxmlElement): sectPr = ZeroOrOne('w:sectPr', successors=_tag_seq[35:]) del _tag_seq + def _insert_border(self, border): + self.insert(0, border) + return border + + @property + def border_top(self): + border = self.border + if border is None: + return None + return border.top + + @border_top.setter + def border_top(self, value): + border = self.get_or_add_border() + border.border_top = value + + @property def first_line_indent(self): """ @@ -346,3 +364,111 @@ def insert_tab_in_order(self, pos, align, leader): return new_tab self.append(new_tab) return new_tab + +class CT_Border(BaseOxmlElement): + + _border_tag_seq = ( + 'w:top', 'w:left', 'w:bottom', 'w:right' + ) + + top = ZeroOrOne('w:top', successors=()) + left = ZeroOrOne('w:left', successors=()) + bottom = ZeroOrOne('w:bottom', successors=()) + right = ZeroOrOne('w:right', successors=()) + + @property + def border_top(self): + top = self.top + if top is None: + return None + return top + + @border_top.setter + def border_top(self, value): + if value is None and self.top is None: + return + top = self.get_or_add_top() + top.type = 'single' + top.size = value + top.space = 0 + top.color = 'auto' + + @property + def border_left(self): + left = self.left + if left is None: + return None + return left + + @border_left.setter + def border_left(self, value): + if value is None and self.left is None: + return + left = self.get_or_add_left() + left.type = 'single' + left.size = value + left.space = 0 + left.color = 'auto' + + @property + def border_bottom(self): + bottom = self.bottom + if bottom is None: + return None + return bottom + + @border_bottom.setter + def border_bottom(self, value): + if value is None and self.bottom is None: + return + bottom = self.get_or_add_bottom() + bottom.type = 'single' + bottom.size = value + bottom.space = 0 + bottom.color = 'auto' + + @property + def border_right(self): + right = self.right + if right is None: + return None + return right + + @border_right.setter + def border_right(self, value): + if value is None and self.right is None: + return + right = self.get_or_add_right() + right.type = 'single' + right.size = value + right.space = 0 + right.color = 'auto' + + +class CT_BorderTop(BaseOxmlElement): + + type = OptionalAttribute('w:val', ST_String) + size = OptionalAttribute('w:sz', ST_TwipsMeasure) + space = OptionalAttribute('w:space', XsdInt) + color = OptionalAttribute('w:color', ST_String) + +class CT_BorderLeft(BaseOxmlElement): + + type = OptionalAttribute('w:val', ST_String) + size = OptionalAttribute('w:sz', ST_TwipsMeasure) + space = OptionalAttribute('w:space', XsdInt) + color = OptionalAttribute('w:color', ST_String) + +class CT_BorderBottom(BaseOxmlElement): + + type = OptionalAttribute('w:val', ST_String) + size = OptionalAttribute('w:sz', ST_TwipsMeasure) + space = OptionalAttribute('w:space', XsdInt) + color = OptionalAttribute('w:color', ST_String) + +class CT_BorderRight(BaseOxmlElement): + + type = OptionalAttribute('w:val', ST_String) + size = OptionalAttribute('w:sz', ST_TwipsMeasure) + space = OptionalAttribute('w:space', XsdInt) + color = OptionalAttribute('w:color', ST_String) diff --git a/docx/text/parfmt.py b/docx/text/parfmt.py index 37206729c..dbb2ff8a0 100644 --- a/docx/text/parfmt.py +++ b/docx/text/parfmt.py @@ -39,6 +39,34 @@ def alignment(self, value): pPr = self._element.get_or_add_pPr() pPr.jc_val = value + # @property + # def border(self): + # pPr = self._element.pPr + # if pPr is None: + # return None + # return pPr.border + # + # @border.setter + # def border(self, value): + # pPr = self._element.get_or_add_pPr() + # pPr.border = value + + @property + def border(self): + pPr = self._element.pPr + if pPr is None: + return None + return pPr.border + + @border.setter + def border(self, value): + pPr = self._element.get_or_add_pPr() + border = pPr.get_or_add_border() + border.border_top = value + border.border_left = value + border.border_bottom = value + border.border_right = value + @property def first_line_indent(self): """ From 320bd59c3bf5bb665d281dbd7625dbabf4992813 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 10:45:13 -0300 Subject: [PATCH 08/14] Delete python-package.yml --- .github/workflows/python-package.yml | 43 ---------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml deleted file mode 100644 index 3bc1e336e..000000000 --- a/.github/workflows/python-package.yml +++ /dev/null @@ -1,43 +0,0 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: Python package - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: [3.8] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Build - run: | - python setup.py - - name: Test with pytest - run: | - pytest From 8b94119fb661e65976a298c935c4a2e35657b32b Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 19:54:09 -0300 Subject: [PATCH 09/14] teste --- docx/oxml/text/parfmt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docx/oxml/text/parfmt.py b/docx/oxml/text/parfmt.py index bc0408237..25068cbbe 100644 --- a/docx/oxml/text/parfmt.py +++ b/docx/oxml/text/parfmt.py @@ -407,7 +407,7 @@ def border_left(self, value): left = self.get_or_add_left() left.type = 'single' left.size = value - left.space = 0 + left.space = 1 left.color = 'auto' @property @@ -441,7 +441,7 @@ def border_right(self, value): right = self.get_or_add_right() right.type = 'single' right.size = value - right.space = 0 + right.space = 1 right.color = 'auto' From 26d53bec83edce57273ac7ef3f94cecb1884339b Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 20:06:49 -0300 Subject: [PATCH 10/14] teste --- docx/text/parfmt.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docx/text/parfmt.py b/docx/text/parfmt.py index dbb2ff8a0..210d9f745 100644 --- a/docx/text/parfmt.py +++ b/docx/text/parfmt.py @@ -51,14 +51,14 @@ def alignment(self, value): # pPr = self._element.get_or_add_pPr() # pPr.border = value - @property - def border(self): - pPr = self._element.pPr - if pPr is None: - return None - return pPr.border - - @border.setter + # @property + # def border(self): + # pPr = self._element.pPr + # if pPr is None: + # return None + # return pPr.border + # + # @border.setter def border(self, value): pPr = self._element.get_or_add_pPr() border = pPr.get_or_add_border() From 73b65e3a2fa2608d2bb97e1022edbd61a8c5791d Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 20:15:59 -0300 Subject: [PATCH 11/14] teste --- docx/oxml/text/parfmt.py | 104 +++++++++++++++++++-------------------- docx/text/parfmt.py | 30 +++++++++-- 2 files changed, 77 insertions(+), 57 deletions(-) diff --git a/docx/oxml/text/parfmt.py b/docx/oxml/text/parfmt.py index 25068cbbe..2f8f436ca 100644 --- a/docx/oxml/text/parfmt.py +++ b/docx/oxml/text/parfmt.py @@ -376,99 +376,99 @@ class CT_Border(BaseOxmlElement): bottom = ZeroOrOne('w:bottom', successors=()) right = ZeroOrOne('w:right', successors=()) - @property - def border_top(self): - top = self.top - if top is None: - return None - return top - - @border_top.setter - def border_top(self, value): + # @property + # def border_top(self): + # top = self.top + # if top is None: + # return None + # return top + # + # @border_top.setter + def border_top(self, size, space, type): if value is None and self.top is None: return top = self.get_or_add_top() - top.type = 'single' - top.size = value - top.space = 0 + top.type = type + top.size = size + top.space = space top.color = 'auto' - @property - def border_left(self): - left = self.left - if left is None: - return None - return left - - @border_left.setter - def border_left(self, value): + # @property + # def border_left(self): + # left = self.left + # if left is None: + # return None + # return left + # + # @border_left.setter + def border_left(self, size, space, type): if value is None and self.left is None: return left = self.get_or_add_left() - left.type = 'single' - left.size = value - left.space = 1 + left.type = type + left.size = size + left.space = space left.color = 'auto' - @property - def border_bottom(self): - bottom = self.bottom - if bottom is None: - return None - return bottom - - @border_bottom.setter - def border_bottom(self, value): + # @property + # def border_bottom(self): + # bottom = self.bottom + # if bottom is None: + # return None + # return bottom + # + # @border_bottom.setter + def border_bottom(self, size, space, type): if value is None and self.bottom is None: return bottom = self.get_or_add_bottom() - bottom.type = 'single' - bottom.size = value - bottom.space = 0 + bottom.type = type + bottom.size = size + bottom.space = space bottom.color = 'auto' - @property - def border_right(self): - right = self.right - if right is None: - return None - return right - - @border_right.setter - def border_right(self, value): + # @property + # def border_right(self): + # right = self.right + # if right is None: + # return None + # return right + # + # @border_right.setter + def border_right(self, size, space, type): if value is None and self.right is None: return right = self.get_or_add_right() - right.type = 'single' - right.size = value - right.space = 1 + right.type = type + right.size = size + right.space = space right.color = 'auto' class CT_BorderTop(BaseOxmlElement): type = OptionalAttribute('w:val', ST_String) - size = OptionalAttribute('w:sz', ST_TwipsMeasure) + size = OptionalAttribute('w:sz', XsdInt) space = OptionalAttribute('w:space', XsdInt) color = OptionalAttribute('w:color', ST_String) class CT_BorderLeft(BaseOxmlElement): type = OptionalAttribute('w:val', ST_String) - size = OptionalAttribute('w:sz', ST_TwipsMeasure) + size = OptionalAttribute('w:sz', XsdInt) space = OptionalAttribute('w:space', XsdInt) color = OptionalAttribute('w:color', ST_String) class CT_BorderBottom(BaseOxmlElement): type = OptionalAttribute('w:val', ST_String) - size = OptionalAttribute('w:sz', ST_TwipsMeasure) + size = OptionalAttribute('w:sz', XsdInt) space = OptionalAttribute('w:space', XsdInt) color = OptionalAttribute('w:color', ST_String) class CT_BorderRight(BaseOxmlElement): type = OptionalAttribute('w:val', ST_String) - size = OptionalAttribute('w:sz', ST_TwipsMeasure) + size = OptionalAttribute('w:sz', XsdInt) space = OptionalAttribute('w:space', XsdInt) color = OptionalAttribute('w:color', ST_String) diff --git a/docx/text/parfmt.py b/docx/text/parfmt.py index 210d9f745..d24b30f30 100644 --- a/docx/text/parfmt.py +++ b/docx/text/parfmt.py @@ -59,13 +59,33 @@ def alignment(self, value): # return pPr.border # # @border.setter - def border(self, value): + def border(self, size=4, space=0, type='single'): pPr = self._element.get_or_add_pPr() border = pPr.get_or_add_border() - border.border_top = value - border.border_left = value - border.border_bottom = value - border.border_right = value + border.border_top(size, space, type) + border.border_left(size, space, type) + border.border_bottom(size, space, type) + border.border_right(size, space, type) + + def border_top(self, size=4, space=0, type='single'): + pPr = self._element.get_or_add_pPr() + border = pPr.get_or_add_border() + border.border_top(size, space, type) + + def border_left(self, size=4, space=0, type='single'): + pPr = self._element.get_or_add_pPr() + border = pPr.get_or_add_border() + border.border_left(size, space, type) + + def border_bottom(self, size=4, space=0, type='single'): + pPr = self._element.get_or_add_pPr() + border = pPr.get_or_add_border() + border.border_bottom(size, space, type) + + def border_right(self, size=4, space=0, type='single'): + pPr = self._element.get_or_add_pPr() + border = pPr.get_or_add_border() + border.border_right(size, space, type) @property def first_line_indent(self): From eb409f4d43ead64cf76cd171544bc029eabaaa70 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 20:16:31 -0300 Subject: [PATCH 12/14] =?UTF-8?q?Vers=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docx/__init__.py b/docx/__init__.py index 1183fcee0..30e46bdcb 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -2,7 +2,7 @@ from docx.api import Document # noqa -__version__ = "0.8.11.2" +__version__ = "0.8.11.3" # register custom Part classes with opc package reader From c5faa3c6f77cc20536a47370bcaa3de95a285df4 Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 20:23:39 -0300 Subject: [PATCH 13/14] Fix --- docx/oxml/text/parfmt.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docx/oxml/text/parfmt.py b/docx/oxml/text/parfmt.py index 2f8f436ca..077de1d91 100644 --- a/docx/oxml/text/parfmt.py +++ b/docx/oxml/text/parfmt.py @@ -65,18 +65,18 @@ def _insert_border(self, border): self.insert(0, border) return border - @property - def border_top(self): - border = self.border - if border is None: - return None - return border.top - - @border_top.setter - def border_top(self, value): - border = self.get_or_add_border() - border.border_top = value - + # @property + # def border_top(self): + # border = self.border + # if border is None: + # return None + # return border.top + # + # @border_top.setter + # def border_top(self, value): + # border = self.get_or_add_border() + # border.border_top = value + # @property def first_line_indent(self): From bf21f667ccb6927341d42bc4f43134a93041733f Mon Sep 17 00:00:00 2001 From: phgrigorio Date: Fri, 1 Oct 2021 20:26:15 -0300 Subject: [PATCH 14/14] Fix --- docx/oxml/text/parfmt.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docx/oxml/text/parfmt.py b/docx/oxml/text/parfmt.py index 077de1d91..840e75ade 100644 --- a/docx/oxml/text/parfmt.py +++ b/docx/oxml/text/parfmt.py @@ -385,8 +385,8 @@ class CT_Border(BaseOxmlElement): # # @border_top.setter def border_top(self, size, space, type): - if value is None and self.top is None: - return + # if value is None and self.top is None: + # return top = self.get_or_add_top() top.type = type top.size = size @@ -402,8 +402,8 @@ def border_top(self, size, space, type): # # @border_left.setter def border_left(self, size, space, type): - if value is None and self.left is None: - return + # if value is None and self.left is None: + # return left = self.get_or_add_left() left.type = type left.size = size @@ -419,8 +419,8 @@ def border_left(self, size, space, type): # # @border_bottom.setter def border_bottom(self, size, space, type): - if value is None and self.bottom is None: - return + # if value is None and self.bottom is None: + # return bottom = self.get_or_add_bottom() bottom.type = type bottom.size = size @@ -436,8 +436,8 @@ def border_bottom(self, size, space, type): # # @border_right.setter def border_right(self, size, space, type): - if value is None and self.right is None: - return + # if value is None and self.right is None: + # return right = self.get_or_add_right() right.type = type right.size = size