Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/api/enum/WdTabAlignment.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.. _WdTabAlignment:

``WD_TAB_ALIGNMENT``
====================

Specifies the tab stop alignment to apply.

----

LEFT
Left-aligned.

CENTER
Center-aligned.

RIGHT
Right-aligned.

DECIMAL
Decimal-aligned.

BAR
Bar-aligned.

LIST
List-aligned. (deprecated)

CLEAR
Clear an inherited tab stop.

END
Right-aligned. (deprecated)

NUM
Left-aligned. (deprecated)

START
Left-aligned. (deprecated)
26 changes: 26 additions & 0 deletions docs/api/enum/WdTabLeader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. _WdTabLeader:

``WD_TAB_LEADER``
=================

Specifies the character to use as the leader with formatted tabs.

----

SPACES
Spaces. Default.

DOTS
Dots.

DASHES
Dashes.

LINES
Double lines.

HEAVY
A heavy line.

MIDDLE_DOT
A vertically-centered dot.
4 changes: 3 additions & 1 deletion docs/api/enum/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ can be found here:
WdColorIndex
WdLineSpacing
WdOrientation
WdRowAlignment
WdSectionStart
WdStyleType
WdRowAlignment
WdTabAlignment
WdTabLeader
WdTableDirection
WdUnderline
74 changes: 74 additions & 0 deletions docx/enum/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,80 @@ class WD_LINE_SPACING(XmlEnumeration):
)


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.
Expand Down
5 changes: 4 additions & 1 deletion docx/oxml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
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_TabStops
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)
Expand All @@ -190,6 +192,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DKWoods All these changes are merged in here: https://github.com/python-openxml/python-docx/commits/feature/tabstops

..these comments are just so you can know for next time. Check the merged commit for actual specifics.

  • w:tab/CT_TabStop is not needed to pass this test. It will have to wait it's turn :) Dropped from this commit.


Expand Down
28 changes: 27 additions & 1 deletion docx/oxml/text/parfmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
Custom element classes related to paragraph properties (CT_PPr).
"""

from ...enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
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 (
Expand Down Expand Up @@ -315,8 +317,32 @@ class CT_Spacing(BaseOxmlElement):
lineRule = OptionalAttribute('w:lineRule', WD_LINE_SPACING)


class CT_TabStop(BaseOxmlElement):
"""
``<w:tab>`` 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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CT_TabStop is not needed to pass this test. Dropped from this commit.


class CT_TabStops(BaseOxmlElement):
"""
``<w:tabs>`` 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
73 changes: 73 additions & 0 deletions docx/text/tabstops.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
)

from ..shared import ElementProxy
from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER


class TabStops(ElementProxy):
Expand All @@ -25,6 +26,19 @@ 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.
Expand All @@ -51,6 +65,25 @@ def __len__(self):
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*. 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):
"""
Expand All @@ -63,3 +96,43 @@ class TabStop(ElementProxy):
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.
"""
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`.
"""
return self._tab.leader

@leader.setter
def leader(self, value):
self._tab.leader = value

@property
def position(self):
"""
The distance (in EMU) of this tab stop from the inside edge of the
paragraph. May be positive or negative.
"""
return self._tab.pos

@position.setter
def position(self, value):
if self._tab.pos != value:
self._tab.getparent().insert_tab_in_order(value, self._tab.val,
self._tab.leader)
self._tab.getparent().remove(self._element)
Loading