aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py390
1 files changed, 390 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py b/.venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py
new file mode 100644
index 00000000..df381d2b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py
@@ -0,0 +1,390 @@
+# Copyright (c) 2010-2024 openpyxl
+
+import atexit
+from collections import defaultdict
+from io import BytesIO
+import os
+from tempfile import NamedTemporaryFile
+from warnings import warn
+
+from openpyxl.xml.functions import xmlfile
+from openpyxl.xml.constants import SHEET_MAIN_NS
+
+from openpyxl.comments.comment_sheet import CommentRecord
+from openpyxl.packaging.relationship import Relationship, RelationshipList
+from openpyxl.styles.differential import DifferentialStyle
+
+from .dimensions import SheetDimension
+from .hyperlink import HyperlinkList
+from .merge import MergeCell, MergeCells
+from .related import Related
+from .table import TablePartList
+
+from openpyxl.cell._writer import write_cell
+
+
+ALL_TEMP_FILES = []
+
+@atexit.register
+def _openpyxl_shutdown():
+ for path in ALL_TEMP_FILES:
+ if os.path.exists(path):
+ os.remove(path)
+
+
+def create_temporary_file(suffix=''):
+ fobj = NamedTemporaryFile(mode='w+', suffix=suffix,
+ prefix='openpyxl.', delete=False)
+ filename = fobj.name
+ fobj.close()
+ ALL_TEMP_FILES.append(filename)
+ return filename
+
+
+class WorksheetWriter:
+
+
+ def __init__(self, ws, out=None):
+ self.ws = ws
+ self.ws._hyperlinks = []
+ self.ws._comments = []
+ if out is None:
+ out = create_temporary_file()
+ self.out = out
+ self._rels = RelationshipList()
+ self.xf = self.get_stream()
+ next(self.xf) # start generator
+
+
+ def write_properties(self):
+ props = self.ws.sheet_properties
+ self.xf.send(props.to_tree())
+
+
+ def write_dimensions(self):
+ """
+ Write worksheet size if known
+ """
+ ref = getattr(self.ws, 'calculate_dimension', None)
+ if ref:
+ dim = SheetDimension(ref())
+ self.xf.send(dim.to_tree())
+
+
+ def write_format(self):
+ self.ws.sheet_format.outlineLevelCol = self.ws.column_dimensions.max_outline
+ fmt = self.ws.sheet_format
+ self.xf.send(fmt.to_tree())
+
+
+ def write_views(self):
+ views = self.ws.views
+ self.xf.send(views.to_tree())
+
+
+ def write_cols(self):
+ cols = self.ws.column_dimensions
+ self.xf.send(cols.to_tree())
+
+
+ def write_top(self):
+ """
+ Write all elements up to rows:
+ properties
+ dimensions
+ views
+ format
+ cols
+ """
+ self.write_properties()
+ self.write_dimensions()
+ self.write_views()
+ self.write_format()
+ self.write_cols()
+
+
+ def rows(self):
+ """Return all rows, and any cells that they contain"""
+ # order cells by row
+ rows = defaultdict(list)
+ for (row, col), cell in sorted(self.ws._cells.items()):
+ rows[row].append(cell)
+
+ # add empty rows if styling has been applied
+ for row in self.ws.row_dimensions.keys() - rows.keys():
+ rows[row] = []
+
+ return sorted(rows.items())
+
+
+ def write_rows(self):
+ xf = self.xf.send(True)
+
+ with xf.element("sheetData"):
+ for row_idx, row in self.rows():
+ self.write_row(xf, row, row_idx)
+
+ self.xf.send(None) # return control to generator
+
+
+ def write_row(self, xf, row, row_idx):
+ attrs = {'r': f"{row_idx}"}
+ dims = self.ws.row_dimensions
+ attrs.update(dims.get(row_idx, {}))
+
+ with xf.element("row", attrs):
+
+ for cell in row:
+ if cell._comment is not None:
+ comment = CommentRecord.from_cell(cell)
+ self.ws._comments.append(comment)
+ if (
+ cell._value is None
+ and not cell.has_style
+ and not cell._comment
+ ):
+ continue
+ write_cell(xf, self.ws, cell, cell.has_style)
+
+
+ def write_protection(self):
+ prot = self.ws.protection
+ if prot:
+ self.xf.send(prot.to_tree())
+
+
+ def write_scenarios(self):
+ scenarios = self.ws.scenarios
+ if scenarios:
+ self.xf.send(scenarios.to_tree())
+
+
+ def write_filter(self):
+ flt = self.ws.auto_filter
+ if flt:
+ self.xf.send(flt.to_tree())
+
+
+ def write_sort(self):
+ """
+ As per discusion with the OOXML Working Group global sort state is not required.
+ openpyxl never reads it from existing files
+ """
+ pass
+
+
+ def write_merged_cells(self):
+ merged = self.ws.merged_cells
+ if merged:
+ cells = [MergeCell(str(ref)) for ref in self.ws.merged_cells]
+ self.xf.send(MergeCells(mergeCell=cells).to_tree())
+
+
+ def write_formatting(self):
+ df = DifferentialStyle()
+ wb = self.ws.parent
+ for cf in self.ws.conditional_formatting:
+ for rule in cf.rules:
+ if rule.dxf and rule.dxf != df:
+ rule.dxfId = wb._differential_styles.add(rule.dxf)
+ self.xf.send(cf.to_tree())
+
+
+ def write_validations(self):
+ dv = self.ws.data_validations
+ if dv:
+ self.xf.send(dv.to_tree())
+
+
+ def write_hyperlinks(self):
+
+ links = self.ws._hyperlinks
+
+ for link in links:
+ if link.target:
+ rel = Relationship(type="hyperlink", TargetMode="External", Target=link.target)
+ self._rels.append(rel)
+ link.id = rel.id
+
+ if links:
+ self.xf.send(HyperlinkList(links).to_tree())
+
+
+ def write_print(self):
+ print_options = self.ws.print_options
+ if print_options:
+ self.xf.send(print_options.to_tree())
+
+
+ def write_margins(self):
+ margins = self.ws.page_margins
+ if margins:
+ self.xf.send(margins.to_tree())
+
+
+ def write_page(self):
+ setup = self.ws.page_setup
+ if setup:
+ self.xf.send(setup.to_tree())
+
+
+ def write_header(self):
+ hf = self.ws.HeaderFooter
+ if hf:
+ self.xf.send(hf.to_tree())
+
+
+ def write_breaks(self):
+ brks = (self.ws.row_breaks, self.ws.col_breaks)
+ for brk in brks:
+ if brk:
+ self.xf.send(brk.to_tree())
+
+
+ def write_drawings(self):
+ if self.ws._charts or self.ws._images:
+ rel = Relationship(type="drawing", Target="")
+ self._rels.append(rel)
+ drawing = Related()
+ drawing.id = rel.id
+ self.xf.send(drawing.to_tree("drawing"))
+
+
+ def write_legacy(self):
+ """
+ Comments & VBA controls use VML and require an additional element
+ that is no longer in the specification.
+ """
+ if (self.ws.legacy_drawing is not None or self.ws._comments):
+ legacy = Related(id="anysvml")
+ self.xf.send(legacy.to_tree("legacyDrawing"))
+
+
+ def write_tables(self):
+ tables = TablePartList()
+
+ for table in self.ws.tables.values():
+ if not table.tableColumns:
+ table._initialise_columns()
+ if table.headerRowCount:
+ try:
+ row = self.ws[table.ref][0]
+ for cell, col in zip(row, table.tableColumns):
+ if cell.data_type != "s":
+ warn("File may not be readable: column headings must be strings.")
+ col.name = str(cell.value)
+ except TypeError:
+ warn("Column headings are missing, file may not be readable")
+ rel = Relationship(Type=table._rel_type, Target="")
+ self._rels.append(rel)
+ table._rel_id = rel.Id
+ tables.append(Related(id=rel.Id))
+
+ if tables:
+ self.xf.send(tables.to_tree())
+
+
+ def get_stream(self):
+ with xmlfile(self.out) as xf:
+ with xf.element("worksheet", xmlns=SHEET_MAIN_NS):
+ try:
+ while True:
+ el = (yield)
+ if el is True:
+ yield xf
+ elif el is None: # et_xmlfile chokes
+ continue
+ else:
+ xf.write(el)
+ except GeneratorExit:
+ pass
+
+
+ def write_tail(self):
+ """
+ Write all elements after the rows
+ calc properties
+ protection
+ protected ranges #
+ scenarios
+ filters
+ sorts # always ignored
+ data consolidation #
+ custom views #
+ merged cells
+ phonetic properties #
+ conditional formatting
+ data validation
+ hyperlinks
+ print options
+ page margins
+ page setup
+ header
+ row breaks
+ col breaks
+ custom properties #
+ cell watches #
+ ignored errors #
+ smart tags #
+ drawing
+ drawingHF #
+ background #
+ OLE objects #
+ controls #
+ web publishing #
+ tables
+ """
+ self.write_protection()
+ self.write_scenarios()
+ self.write_filter()
+ self.write_merged_cells()
+ self.write_formatting()
+ self.write_validations()
+ self.write_hyperlinks()
+ self.write_print()
+ self.write_margins()
+ self.write_page()
+ self.write_header()
+ self.write_breaks()
+ self.write_drawings()
+ self.write_legacy()
+ self.write_tables()
+
+
+ def write(self):
+ """
+ High level
+ """
+ self.write_top()
+ self.write_rows()
+ self.write_tail()
+ self.close()
+
+
+ def close(self):
+ """
+ Close the context manager
+ """
+ if self.xf:
+ self.xf.close()
+
+
+ def read(self):
+ """
+ Close the context manager and return serialised XML
+ """
+ self.close()
+ if isinstance(self.out, BytesIO):
+ return self.out.getvalue()
+ with open(self.out, "rb") as src:
+ out = src.read()
+
+ return out
+
+
+ def cleanup(self):
+ """
+ Remove tempfile
+ """
+ os.remove(self.out)
+ ALL_TEMP_FILES.remove(self.out)