# GroupDocs.Editor for Python via .NET — Complete Documentation
> Native Python library that loads Word, Excel, PowerPoint, PDF, email, eBook, and text documents, converts them to editable HTML/CSS, and saves them back — or to another format — on Windows, Linux, and macOS. No Microsoft Office or OpenOffice required.
---
## Adding class name to input controls
Path: /editor/python-net/adding-class-name-to-input-controls/
Almost all formats within WordProcessing format family, like DOC(X/M), ODT etc., support input controls of different kinds. WordProcessing documents can contain different buttons, textboxes, check-boxes, combo-boxes, input fields, dropdown lists, radio-buttons, date/time pickers and much more, which are internally present as Structured Document Tag (SDT) entities or Fields ("Insert > Quick Parts > Document Property/Field"). **[GroupDocs.Editor](https://products.groupdocs.com/editor/python-net)** supports all of these entities and preserves them while converting the document to the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance. Finally, when generating a HTML document from [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) in order to edit it in the WYSIWYG HTML-editor, these input controls are translated into the most appropriate HTML structures and elements. Additionally, when input document contains not only input controls, but also user-entered data, this data is also preserved and will be present in the output HTML document.
In some specific use-cases the end-user may require not to edit the entire document content, but only edit and/or gather data, entered into the input controls. For such case it is required to identify all these input controls in some way in order to fetching them, to distinguish them from all other HTML elements, when working on client-side. For achieving this purpose the GroupDocs.Editor has an ability to set an unique user-provided CSS class name for all such input controls in HTML markup. The [WordProcessingEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingeditoptions) class contains an [`input_controls_class_name`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingeditoptions/) property for this purpose.
By default it has a `None` value — class names are not applied to the HTML elements. However, if user will set a valid class name, all the input control elements (like INPUT, BUTTON, SELECT etc.) will have a "class" HTML attribute with specified class name. For example:
```python
from groupdocs.editor.options import WordProcessingEditOptions
edit_options = WordProcessingEditOptions()
edit_options.input_controls_class_name = "editable-field"
```
Finally, when "class" attribute with specified class name is applied to all HTML elements, that represent input controls, client code is able to work with them by, for example, traversing the HTML DOM and gathering and/or manipulating with data.
## Complete example
The code example below demonstrates editing the sample DOCX document twice: the first time with default `WordProcessingEditOptions`, where no custom class name is specified, and the second time with a custom value in the `input_controls_class_name` property. The example then reports how many input-control elements carry the custom class name in each version — none without it, and several once it is applied.
{{< tabs "code-example-adding-class-name-to-input-controls">}}
{{< tab "add_class_name_to_input_controls.py" >}}
```python
import os
from groupdocs.editor import Editor, License
from groupdocs.editor.options import WordProcessingEditOptions
def add_class_name_to_input_controls():
# Optionally set a license
license_path = os.path.abspath("./GroupDocs.Editor.lic")
if os.path.exists(license_path):
License().set_license(license_path)
# Default edit options, where no custom class name is applied
options_without_class_name = WordProcessingEditOptions()
# Custom edit options, where a class name is applied to input controls
options_with_class_name = WordProcessingEditOptions()
options_with_class_name.input_controls_class_name = "editable-field"
with Editor("./sample-document.docx") as editor:
# Edit the document twice with both option sets
html_without_class_name = editor.edit(options_without_class_name).get_embedded_html()
html_with_class_name = editor.edit(options_with_class_name).get_embedded_html()
# The custom class name is applied to every input-control element in the HTML
print("Occurrences of 'editable-field' without custom class name:",
html_without_class_name.count("editable-field"))
print("Occurrences of 'editable-field' with custom class name:",
html_with_class_name.count("editable-field"))
if __name__ == "__main__":
add_class_name_to_input_controls()
```
{{< /tab >}}
{{< tab "sample-document.docx" >}}
{{< tab-text >}}
`sample-document.docx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-word/adding-class-name-to-input-controls/sample-document.docx) to download it.
{{< /tab-text >}}
{{< /tab >}}
{{< tab "add-class-name-to-input-controls.txt" >}}
```text
Occurrences of 'editable-field' without custom class name: 0
Occurrences of 'editable-field' with custom class name: 7
```
[Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-word/adding-class-name-to-input-controls/add_class_name_to_input_controls/add-class-name-to-input-controls.txt)
{{< /tab >}}
{{< /tabs >}}
When the `class` attribute with the specified class name is applied to all HTML elements that represent input controls, the client code is able to work with them — for example, by traversing the HTML DOM and gathering or manipulating the data.
---
## Features Overview
Path: /editor/python-net/features-overview/
GroupDocs.Editor for Python via .NET allows you to edit files and documents across a wide range of [supported document types]({{< ref "editor/python-net/getting-started/supported-document-formats.md" >}}). Below is a short list of possible actions:
## Document editing
The main feature of GroupDocs.Editor is an ability to edit most popular document formats using front-end WYSIWYG editors without any additional applications. No Open Office or MS Office is required to edit Word Processing documents, Spreadsheets or Presentations. You can just load a document via GroupDocs.Editor into any WYSIWYG editor, edit the document the way you want, and save it back to the original document format.
## Editing options and output customizations
GroupDocs.Editor provides a set of options to customize the editing process depending on the document type:
* Word Processing documents - ability to edit a document in flow or paged mode; consider language information for multi-language document editing; manage font extraction to provide the same document editing and appearance behaviour in different environments.
* Spreadsheets - supports multi-tabbed spreadsheet editing by allowing you to specify the index of the currently edited worksheet.
* Comma-Separated Values and Tab-Separated Values - options to specify the separator; flexible numeric and date conversion; memory usage optimization for large files.
* XML files - fix incorrect document structure; URI and e-mail address recognition; highlight and formatting options, etc.
## Document information extraction
GroupDocs.Editor provides an ability to extract basic information about an edited document:
* Document type;
* Document size;
* Pages count;
* etc.
---
## Get HTML markup in different forms
Path: /editor/python-net/get-html-markup-in-different-forms/
> This demonstration shows how to open an input document, convert it to an intermediate [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), and get HTML markup in different forms depending on client requirements.
## Preparations
When an input document is loaded into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class and opened for editing by transforming it to the intermediate [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class, it is possible to generate and get HTML markup in different forms.
First of all the user needs to load the document into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class and open it for editing, which is demonstrated in the code below.
```python
from groupdocs.editor import Editor
from groupdocs.editor.options import WordProcessingLoadOptions
load_options = WordProcessingLoadOptions()
editor = Editor("document.docx", load_options) # passing path and load options to the constructor
document = editor.edit() # opening the document for editing
```
The piece of code above prepares a ready-to-use instance of the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class, that contains the original document in its own intermediate format and is able to generate HTML markup in different forms.
## Getting the whole HTML content
The most default and standard method for generating HTML markup is the parameterless [`get_content()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getcontent) method:
```python
html_content = document.get_content()
```
If the document has external resources (stylesheets, fonts, images), they are referenced via different HTML elements: stylesheets are specified through `LINK` elements, while images — through `IMG`. When using the [`get_content()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getcontent) method, such external resources will be referenced by external links. For example:
```html
```
## Getting HTML BODY content
A lot of HTML WYSIWYG editors are not able to process the whole HTML document, with a `HEAD` section and so on. They are only able to process the inner content of the HTML->BODY element. In order to obtain such part of the HTML markup, the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class contains the [`get_body_content()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getbodycontent) method:
```python
body_content = document.get_body_content()
```
It is also possible to pass an external images template, which is added to every URL in the `src` attribute of every `IMG` tag found inside the HTML->BODY markup:
```python
external_images_template = "http://www.mywebsite.com/images/id="
prefixed_body_content = document.get_body_content(external_images_template)
```
## Getting the stylesheet content
The [`get_css_content()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getcsscontent) method returns the CSS stylesheet(s) of the document. It can be called without arguments, or with prefixes for external images and fonts referenced from the stylesheets:
```python
css_content = document.get_css_content()
```
## Getting base64-encoded content
Sometimes it is necessary to obtain all the content of the whole document with all used resources in a single string. GroupDocs.Editor allows to do this with the [`get_embedded_html()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getembeddedhtml) method:
```python
embedded_html_content = document.get_embedded_html()
```
In such a string all stylesheets will be placed into `STYLE` elements in the HTML->HEAD section, all images in `IMG` elements will be serialized with base64 encoding and placed directly in the `src` attributes. All fonts and images, which are used in the stylesheets, will also be serialized and stored in the appropriate locations. Such a string is fully autonomous and self-sufficient.
## Complete code example
The example below loads a document, opens it for editing, and prints the lengths of the HTML markup obtained in different forms.
{{< tabs "code-example-get-html-markup-in-different-forms">}}
{{< tab "get_html_markup_in_different_forms.py" >}}
```python
import os
from groupdocs.editor import Editor, License
def get_html_markup_in_different_forms():
# Optionally set a license
license_path = os.path.abspath("./GroupDocs.Editor.lic")
if os.path.exists(license_path):
License().set_license(license_path)
with Editor("./sample-document.docx") as editor:
document = editor.edit()
# Generate the HTML markup in different forms and inspect their sizes
print("Whole content length:", len(document.get_content()))
print("Body content length:", len(document.get_body_content()))
print("Embedded (base64) content length:", len(document.get_embedded_html()))
# get_css_content() returns the stylesheet(s) of the document
css = document.get_css_content()
print("CSS stylesheets count:", len(css))
document.dispose()
if __name__ == "__main__":
get_html_markup_in_different_forms()
```
{{< /tab >}}
{{< tab "sample-document.docx" >}}
{{< tab-text >}}
`sample-document.docx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/editabledocument/get-html-markup-in-different-forms/sample-document.docx) to download it.
{{< /tab-text >}}
{{< /tab >}}
{{< tab "get-html-markup-in-different-forms.txt" >}}
```text
Whole content length: 31654
Body content length: 31461
Embedded (base64) content length: 95550
CSS stylesheets count: 1
```
[Download full output](/editor/python-net/_output_files/developer-guide/editabledocument/get-html-markup-in-different-forms/get_html_markup_in_different_forms/get-html-markup-in-different-forms.txt)
{{< /tab >}}
{{< /tabs >}}
---
## Inserting edited worksheet into existing spreadsheet
Path: /editor/python-net/inserting-edited-worksheet-into-existing-spreadsheet/
By default and from the moment when a Spreadsheet module was released to public, the full spreadsheet editing pipeline was the next:
1. Loading spreadsheet in a form of a file or stream into constructor of the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class.
2. Selecting a specific worksheet to edit and specifying its index in the [worksheet_index](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheeteditoptions/worksheetindex) property of [SpreadsheetEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheeteditoptions) class.
3. Opening a spreadsheet for editing by calling [editor.edit()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method, passing [SpreadsheetEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheeteditoptions) instance with selected worksheet into it, and obtaining [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance from this method.
4. Emitting HTML and CSS markup, which represents a content of an edited document, from [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, using different methods like [get_content()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getcontent).
5. Passing this HTML and CSS markup into the WYSIWYG editor, which is located on client-side and is running in the browser.
6. End-user edits the document content in the WYSIWYG editor.
7. Edited document content, in form of HTML and CSS markup, is passed back to the server-side.
8. Creating a new instance of the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class by passing the edited content, obtained from server, into the [from_file](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/fromfile), [from_markup](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkup), or [from_markup_and_resource_folder](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkupandresourcefolder/) static methods (depending from content).
9. Creating an instance of [SpreadsheetSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) class with format of output spreadsheet file.
10. Calling an [editor.save](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method by passing the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), output stream for the document, and a [SpreadsheetSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) options, into it. This will generate a new spreadsheet, which contains only one single worksheet — those worksheet, that was edited on the client-side and which content was passed via [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class into this method.
The last 10th step can be altered — the edited worksheet can be inserted into the original spreadsheet, which was loaded on the 1st step.
[SpreadsheetSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) class contains two properties: integer [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) and boolean flag [insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/). Both of them have "usual" default values: `worksheet_number` has `0` and `insert_as_new_worksheet` is set to `False`.
```python
save_options.worksheet_number = 0
save_options.insert_as_new_worksheet = False
```
By default, when these properties are not touched or at least [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) has a default value `0`, the GroupDocs.Editor will generate new single-worksheet spreadsheet, as before. However, if [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property contains number, distinct from `0`, and valid spreadsheet is loaded into [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class (it is expected to be the original spreadsheet, which was edited, but it actually can be any spreadsheet, even those, which has no relation to the original), then edited worksheet will be **inserted** into given spreadsheet.
## worksheet_number property
[worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property, if it is not a zero, defines, where, at which exact position in the given spreadsheet the new edited worksheet should be inserted. [insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) parameter, which is a boolean flag, determines, how this worksheet should be inserted: should it replace the existing worksheet, that is located on specified position (`False`, default value), or it should be injected between existing worksheets, without rewriting them, and thus increasing the total amount of worksheets in the given spreadsheet by one.
Because default `0` value of [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) parameter is reserved, actual worksheet numbering starts from `1`. This is different from widely used 0-based indexing, however, makes sense, thus it is not an index of a worksheet, but rather number of a worksheet, the same as MS Excel uses, for instance. This means that, for example, for given spreadsheet, that consists of 5 worksheets, 1st one has a `1` worksheet number, and 5th — `5`. If user has specified a worksheet number, which exceeds the total amount of worksheets in spreadsheet, this number will be automatically adjusted to the latest. This means that if, for example, for the same 5-worksheet spreadsheet the user will specify a `6`, `7` or even a very big value, it will be internally set a `5` — number of the latest worksheet.
Along with positive worksheet numbers, the [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property also supports negative numbering, which implies count from the end of spreadsheet. In this case, `-1` is treated as the last worksheet, `-2` — the last but one, and so on. Again, like with positive numbering, in case when number exceeds the amount of worksheets, it will be adjusted to the closest — first worksheet for negative numbers.
Part of source code below explains this numbering system:
```python
save_options = SpreadsheetSaveOptions(SpreadsheetFormats.XLSX)
# let's say we have a spreadsheet with 5 worksheets
save_options.worksheet_number = 0 # default value, given spreadsheet will be ignored and new will be created
# positive numbering
save_options.worksheet_number = 1 # first worksheet
save_options.worksheet_number = 2 # second worksheet
save_options.worksheet_number = 3 # third worksheet
save_options.worksheet_number = 4 # fourth worksheet
save_options.worksheet_number = 5 # fifth worksheet
save_options.worksheet_number = 6 # fifth worksheet, because value '6' exceeds the worksheets amount '5' and thus is adjusted to the closest
# negative numbering
save_options.worksheet_number = -1 # fifth worksheet, which is first from end (last)
save_options.worksheet_number = -2 # fourth worksheet, which is second from end (last but one)
save_options.worksheet_number = -3 # third worksheet, which is third from end
save_options.worksheet_number = -4 # second worksheet, which is fourth from end
save_options.worksheet_number = -5 # first worksheet, which is fifth from end
save_options.worksheet_number = -6 # first worksheet, because value '-6' exceeds the worksheets amount '5' and thus is adjusted to the closest
```
## insert_as_new_worksheet property
[insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) property complements the previously described [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property and is ignored, when [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) is set to `0`. If [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) points on the worksheet of specific number, the [insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) determines how to treat this worksheet number and how to insert the worksheet:
* [insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) property has default `False` value, the existing worksheet in given spreadsheet will be completely erased, and the content of the new worksheet (which is located in the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance) will be putted on this place. As a result, a spreadsheet will preserve the same untouched amount of worksheets, but one of its worksheets (specified by the [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber)) will be replaced onto new one.
* If [insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) property has `True` value, the edited worksheet, obtained from [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, will be injected among existing worksheets in given spreadsheet, so its amount of worksheets will be incremented by one. New worksheet is inserted at position, specified by [worksheet_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber), and all subsequent worksheets (following or preceding) will be shifted to the end or to the beginning accordingly, depending on positive or negative numbering.
Source code below shows, how worksheet number is treated when [insert_as_new_worksheet](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) property is enabled:
```python
save_options = SpreadsheetSaveOptions(SpreadsheetFormats.XLSX)
# let's say we have a spreadsheet with 5 worksheets
save_options.worksheet_number = 0 # default value, given spreadsheet will be ignored, as well as insert_as_new_worksheet
save_options.insert_as_new_worksheet = True # enabling the adding of worksheet instead of replacing
# positive numbering
save_options.worksheet_number = 1 # new worksheet is injected as first, while all following (including 'old' 1st) are shifting to the end
save_options.worksheet_number = 2 # new worksheet is injected as second, while 2nd, 3rd, 4th and 5th are shifting to the end
save_options.worksheet_number = 3 # new worksheet is injected as third, while 3rd, 4th and 5th are shifting to the end
save_options.worksheet_number = 4 # new worksheet is injected as fourth, while 4th and 5th are shifting to the end
save_options.worksheet_number = 5 # new worksheet is injected as fifth, while 5th is shifting to the end and becomes 6th
save_options.worksheet_number = 6 # new worksheet is injected as sixth, it already becomes the latest, none of existing worksheets are shifting to the end
save_options.worksheet_number = 7 # same as previous
# negative numbering
save_options.worksheet_number = -1 # new worksheet is injected as first from end (it becomes sixth if starting from beginning), none of existing worksheets are shifting to the end
save_options.worksheet_number = -2 # new worksheet is injected as second from end (it becomes fifth if starting from beginning), following single worksheet is shifting to the end
save_options.worksheet_number = -3 # new worksheet is injected as third from end (it becomes fourth if starting from beginning), two following worksheets are shifting to the end
save_options.worksheet_number = -4 # new worksheet is injected as fourth from end (it becomes third if starting from beginning), three following worksheets are shifting to the end
save_options.worksheet_number = -5 # new worksheet is injected as fifth from end (it becomes second if starting from beginning), four following worksheets are shifting to the end
save_options.worksheet_number = -6 # new worksheet is injected as sixth from end (it becomes first if starting from beginning), five following worksheets are shifting to the end
save_options.worksheet_number = -7 # same as previous
```
The complete example below loads a spreadsheet, edits its first worksheet, and saves the result by inserting the edited worksheet into the original spreadsheet as a new worksheet, keeping the original worksheets intact.
{{< tabs "code-example-inserting-edited-worksheet-into-existing-spreadsheet">}}
{{< tab "inserting_edited_worksheet_into_existing_spreadsheet.py" >}}
```python
import os
from groupdocs.editor import Editor, EditableDocument, License
from groupdocs.editor.options import SpreadsheetLoadOptions, SpreadsheetEditOptions, SpreadsheetSaveOptions
from groupdocs.editor.formats import SpreadsheetFormats
def inserting_edited_worksheet_into_existing_spreadsheet():
# Optionally set a license
license_path = os.path.abspath("./GroupDocs.Editor.lic")
if os.path.exists(license_path):
License().set_license(license_path)
# Load input spreadsheet to the Editor and specify loading options
with Editor("./sample-spreadsheet.xlsx", SpreadsheetLoadOptions()) as editor:
# Prepare edit options and set the 1st worksheet to edit
edit_options = SpreadsheetEditOptions()
edit_options.worksheet_index = 0 # index is 0-based, so this is the 1st worksheet
# Generate EditableDocument with the original content of the worksheet
worksheet_opened_for_edit = editor.edit(edit_options)
# Get the HTML-markup from the EditableDocument with the original content
original_html = worksheet_opened_for_edit.get_embedded_html()
# Emulate HTML content editing in a WYSIWYG-editor in the browser or somewhere else
edited_html = original_html.replace("
}} {{< /tabs >}} --- ## Supported Document Formats Path: /editor/python-net/supported-document-formats/ The following table indicates the file formats that GroupDocs.Editor for Python via .NET can edit. You can use the input below to filter supported formats by extension. {{< table-filter placeholder="Start typing to find file format" forumUrl="https://forum.groupdocs.com/c/editor/20">}} ## WordProcessing family formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [DOC](https://docs.fileformat.com/word-processing/doc/) | MS Word 97-2007 Binary File Format |  |  |  |  | | [DOCX](https://docs.fileformat.com/word-processing/docx/) | Office Open XML WordProcessingML Macro-Free Document |  |  |  |  | | [DOCM](https://docs.fileformat.com/word-processing/docm/) | Office Open XML WordProcessingML Macro-Enabled Document |  |  |  |  | | [DOT](https://docs.fileformat.com/word-processing/dot/) | MS Word 97-2007 Template |  |  |  |  | | [DOTX](https://docs.fileformat.com/word-processing/dotx/) | Office Open XML WordprocessingML Macro-Free Template |  |  |  |  | | [DOTM](https://docs.fileformat.com/word-processing/dotm/) | Office Open XML WordprocessingML Macro-Enabled Template |  |  |  |  | | FlatOPC | Office Open XML WordprocessingML stored in a flat XML file instead of a ZIP package |  |  |  |  | | [ODT](https://docs.fileformat.com/word-processing/odt/) | Open Document Format Text Document |  |  |  |  | | [OTT](https://docs.fileformat.com/word-processing/ott/) | Open Document Format Text Document Template |  |  |  |  | | [RTF](https://docs.fileformat.com/word-processing/rtf/) | Rich Text Format |  |  |  |  | | WordML | Microsoft Office Word 2003 XML Format — WordProcessingML or WordML |  |  |  |  | ## Spreadsheet family formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [XLS](https://docs.fileformat.com/spreadsheet/xls/) | Excel 97-2003 Binary File Format |  |  |  |  | | [XLT](https://docs.fileformat.com/spreadsheet/xlt/) | Excel 97-2003 Template |  |  |  |  | | [XLSX](https://docs.fileformat.com/spreadsheet/xlsx/) | Office Open XML Workbook Macro-Free |  |  |  |  | | [XLSM](https://docs.fileformat.com/spreadsheet/xlsm/) | Office Open XML Workbook Macro-Enabled |  |  |  |  | | [XLTX](https://docs.fileformat.com/spreadsheet/xltx/) | Office Open XML Template Macro-Free |  |  |  |  | | [XLTM](https://docs.fileformat.com/spreadsheet/xltm/) | Office Open XML Template Macro-Enabled |  |  |  |  | | [XLSB](https://docs.fileformat.com/spreadsheet/xlsb/) | Excel Binary Workbook |  |  |  |  | | [XLAM](https://docs.fileformat.com/spreadsheet/xlam/) | Excel Add-in |  |  |  |  | | SpreadsheetML | Microsoft Office Excel 2002 and Excel 2003 XML Format |  |  |  |  | | [ODS](https://docs.fileformat.com/spreadsheet/ods/) | OpenDocument Spreadsheet |  |  |  |  | | [FODS](https://docs.fileformat.com/spreadsheet/fods/) | Flat OpenDocument Spreadsheet — stored as a single uncompressed XML document |  |  |  |  | | [SXC](https://docs.fileformat.com/spreadsheet/sxc/) | StarOffice or OpenOffice.org Calc XML Spreadsheet |  |  |  |  | | [DIF](https://docs.fileformat.com/spreadsheet/dif/) | Data Interchange Format |  |  |  |  | | DSV | Delimiter Separated Values document (arbitrary delimiter) |  |  |  |  | | [CSV](https://docs.fileformat.com/spreadsheet/csv/) | Comma Separated Values document |  |  |  |  | | [TSV](https://docs.fileformat.com/spreadsheet/tsv/) | Tab Separated Values document |  |  |  |  | ## Presentation family formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [PPT](https://wiki.fileformat.com/presentation/ppt/) | Microsoft PowerPoint 95 Presentation |  |  |  |  | | [PPT](https://wiki.fileformat.com/presentation/ppt/) | Microsoft PowerPoint 97-2003 Presentation |  |  |  |  | | [PPTX](https://wiki.fileformat.com/presentation/pptx/) | Microsoft Office Open XML PresentationML Macro-Free Document |  |  |  |  | | [PPTM](https://wiki.fileformat.com/presentation/pptm/) | Microsoft Office Open XML PresentationML Macro-Enabled Document |  |  |  |  | | [PPS](https://wiki.fileformat.com/presentation/pps/) | Microsoft PowerPoint 97-2003 SlideShow |  |  |  |  | | [PPSX](https://wiki.fileformat.com/presentation/ppsx/) | Microsoft Office Open XML PresentationML Macro-Free SlideShow |  |  |  |  | | [PPSM](https://wiki.fileformat.com/presentation/ppsm/) | Microsoft Office Open XML PresentationML Macro-Enabled SlideShow |  |  |  |  | | [POT](https://wiki.fileformat.com/presentation/pot/) | Microsoft PowerPoint 97-2003 Presentation Template |  |  |  |  | | [POTX](https://wiki.fileformat.com/presentation/potx/) | Microsoft Office Open XML PresentationML Macro-Free Template |  |  |  |  | | [POTM](https://wiki.fileformat.com/presentation/potm/) | Microsoft Office Open XML PresentationML Macro-Enabled Template |  |  |  |  | | [ODP](https://wiki.fileformat.com/presentation/odp/) | OpenDocument Presentation |  |  |  |  | | [OTP](https://wiki.fileformat.com/presentation/otp/) | OpenDocument Presentation template |  |  |  |  | | FODP | Flat XML ODF Presentation |  |  |  |  | ## Fixed-layout family formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [PDF](https://docs.fileformat.com/pdf/) | Portable Document Format |  |  |  |  | | [XPS](https://docs.fileformat.com/page-description-language/xps/) | XML Paper Specification |  |  |  |  | ## Email family formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [EML](https://docs.fileformat.com/email/eml/) | RFC-822 Internet Message Format Standard |  |  |  |  | | [EMLX](https://docs.fileformat.com/email/emlx/) | Apple Mail App format |  |  |  |  | | [MSG](https://docs.fileformat.com/email/msg/) | Microsoft Outlook and Exchange email format |  |  |  |  | | [MBOX](https://docs.fileformat.com/email/mbox/) | Container for collection of electronic mail messages |  |  |  |  | | [TNEF](https://docs.fileformat.com/email/tnef/) | Transport Neutral Encapsulation Format |  |  |  |  | | [MHT](https://docs.fileformat.com/web/mht/) | MIME encapsulation of aggregate HTML documents |  |  |  |  | | [PST](https://docs.fileformat.com/email/pst/) | Personal Storage Table |  |  |  |  | | [OFT](https://docs.fileformat.com/email/oft/) | Outlook MSG file format for message template |  |  |  |  | | [OST](https://docs.fileformat.com/email/oft/) | Offline Storage Table |  |  |  |  | | [VCF](https://docs.fileformat.com/email/vcf/) | Virtual Card Format |  |  |  |  | | [ICS](https://docs.fileformat.com/email/ics/) | Internet Calendaring and Scheduling Core Object Specification (iCalendar) |  |  |  |  | ## eBook family formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [ePub](https://docs.fileformat.com/ebook/epub/) | Electronic Publication |  |  |  |  | | [MOBI](https://docs.fileformat.com/ebook/mobi/) | MobiPocket |  |  |  |  | | [AZW3](https://docs.fileformat.com/ebook/azw3/) | AZW3, also known as Kindle Format 8 (KF8) |  |  |  |  | ## Markup formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [HTML](https://docs.fileformat.com/web/html/) | HyperText Markup Language |  |  |  |  | | [MHTML](https://docs.fileformat.com/web/mhtml/) | MIME Encapsulation of Aggregate HTML |  |  |  |  | | [CHM](https://docs.fileformat.com/web/chm/) | Microsoft Compiled HTML Help |  |  |  |  | | [XML](https://docs.fileformat.com/web/xml/) | eXtensible Markup Language |  |  |  |  | | [JSON](https://docs.fileformat.com/web/json/) | JavaScript Object Notation |  |  |  |  | | [MD](https://docs.fileformat.com/word-processing/md/) | Markdown |  |  |  |  | ## Other formats | Format | Description | Create | Import | Export | Auto Detection | | --- | --- | --- | --- | --- | --- | | [TXT](https://docs.fileformat.com/word-processing/txt/) | Plain Text |  |  |  |  | --- ## Deleting slides from presentation Path: /editor/python-net/deleting-slides-from-presentation/ There are many [presentation](https://docs.fileformat.com/presentation/) document formats — [PPT](https://docs.fileformat.com/presentation/ppt/), [PPTX](https://docs.fileformat.com/presentation/pptx/), [ODP](https://docs.fileformat.com/presentation/odp/) and many more. They all have one common thing — the *slides*. Each presentation document has one or more slides, and these slides may be treated as containers of different content: text, geometric shapes, raster and vector images, animations, video, audio, comments, embedded objects and many more. The GroupDocs.Editor from its beginning had an ability to edit presentations, but only on a per-slide basis: a user can edit only the content of one slide at a time. Initially, when the content of the slide was edited, it was only possible to save it as a new one-slide presentation. In particular, the pipeline was the next: user loads a presentation to the [`Editor`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class, selects the slide to edit with help of [`PresentationEditOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationeditoptions), obtains [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), edits the HTML-content of the slide, saves this content back to the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), and finally generates a new presentation file with one slide using the [`editor.save()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method. Later this pipeline was expanded — it became possible to insert an edited slide into the original presentation using two ways: 1. by replacing the original slide, that was initially set for editing, onto its edited version, 2. and by putting the edited slide to "co-exist" together with its original version before edit. This mechanism is explained in detail in a [separate article](https://docs.groupdocs.com/editor/python-net/inserting-edited-slide-into-existing-presentation/). It is possible not only to edit and replace slides in the resultant presentation, but also delete them from it. The [`PresentationSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions) class contains a [`slide_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumberstodelete/) property, which basically is a list of integers, where each integer represents a number of one slide that should be removed. Slide numbers here are 1-based, so the 1st slide has number `1`, not `0`. With the slides removal feature the GroupDocs.Editor performs the next algorithm during saving the presentation: 1. User edits a content of the slide in the WYSIWYG-editor, passes the edited content to the instance of the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class [using one of its static methods](https://docs.groupdocs.com/editor/python-net/create-editabledocument-from-file-or-markup/), creates and adjusts the [`PresentationSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions), and passes the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, [`PresentationSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions) instance, and output stream or file path for writing to the [`editor.save()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method. 2. If the value of the [`slide_number`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) property in the [`PresentationSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions) instance is set to the default value `0`, the GroupDocs.Editor generates a new presentation with one slide inside it. The [`slide_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumberstodelete/) property is ignored regardless of its value. 3. If the value of the [`slide_number`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) property in the [`PresentationSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions) instance has some non-zero value, the GroupDocs.Editor treats it as a command to insert the edited slide into the original presentation. 4. Depending on the [`insert_as_new_slide`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide) property, the GroupDocs.Editor replaces old slide, specified by the value of the [`slide_number`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) property, on the new one, taken from the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, or puts the new slide to be placed among existing slides without replacing any of them. 5. Finally, when all slide insertions and rearrangements are finished, the GroupDocs.Editor reads the value of the [`slide_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumberstodelete/) property, iterates over all numbers and removes specified slides from the presentation. 6. The final presentation, with updated and deleted slides, is written to the output stream or file. The example below shows a full roundtrip of an input PPTX file: a presentation is loaded, its first slide is converted to the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), then HTML-markup is emitted from the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, modified, and the edited HTML-markup is converted back to another [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance. This edited slide is then inserted into the original presentation, while the [`slide_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumberstodelete/) property is set to remove the original second slide during saving. {{< tabs "code-example-deleting-slides-from-presentation">}} {{< tab "deleting_slides_from_presentation.py" >}} ```python import os from groupdocs.editor import Editor, EditableDocument, License from groupdocs.editor.options import PresentationLoadOptions, PresentationEditOptions, PresentationSaveOptions from groupdocs.editor.formats import PresentationFormats def deleting_slides_from_presentation(): # Optionally set a license license_path = os.path.abspath("./GroupDocs.Editor.lic") if os.path.exists(license_path): License().set_license(license_path) # Load input presentation to the Editor and specify loading options with Editor("./sample-presentation.pptx", PresentationLoadOptions()) as editor: # Prepare edit options and set the 1st slide to edit edit_options = PresentationEditOptions() edit_options.show_hidden_slides = True edit_options.slide_number = 0 # index is 0-based, so this is the 1st slide # Generate EditableDocument with the original content of the slide slide_opened_for_edit = editor.edit(edit_options) # Get the HTML-markup from the EditableDocument with the original content original_html = slide_opened_for_edit.get_embedded_html() # Emulate HTML content editing in a WYSIWYG-editor in the browser or somewhere else edited_html = original_html.replace("
", "
Edited content
")
# Generate EditableDocument with the edited content
worksheet_after_edit = EditableDocument.from_markup(edited_html)
# Prepare save options that insert the edited worksheet into the original spreadsheet
save_options = SpreadsheetSaveOptions(SpreadsheetFormats.XLSX)
save_options.worksheet_number = 1 # 1-based; insert at the 1st position
save_options.insert_as_new_worksheet = True # keep the edited worksheet alongside the original ones
# Save the spreadsheet with the inserted edited worksheet
editor.save(worksheet_after_edit, "./edited-spreadsheet.xlsx", save_options)
worksheet_opened_for_edit.dispose()
worksheet_after_edit.dispose()
print("Saved spreadsheet with the inserted edited worksheet to edited-spreadsheet.xlsx")
if __name__ == "__main__":
inserting_edited_worksheet_into_existing_spreadsheet()
```
{{< /tab >}}
{{< tab "sample-spreadsheet.xlsx" >}}
{{< tab-text >}}
`sample-spreadsheet.xlsx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-excel/inserting-edited-worksheet-into-existing-spreadsheet/sample-spreadsheet.xlsx) to download it.
{{< /tab-text >}}
{{< /tab >}}
{{< tab "edited-spreadsheet.xlsx" >}}
```text
Binary file (XLSX, 66 KB)
```
[Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-excel/inserting-edited-worksheet-into-existing-spreadsheet/inserting_edited_worksheet_into_existing_spreadsheet/edited-spreadsheet.xlsx)
{{< /tab >}}
{{< /tabs >}}
### Additional notes
It is worth mentioning that the described feature doesn't modify the original spreadsheet document, which was originally loaded into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class through its constructor. When saving a spreadsheet and inserting the edited worksheet into existing spreadsheet, the GroupDocs.Editor creates a full and exact copy of the original document, and only then adds or replaces the worksheet onto the edited. So the original document is not touched in any case.
From this point it is clear that the GroupDocs.Editor cannot insert edited worksheet into existing spreadsheet document, if this document is not available. For example, original spreadsheet document was loaded into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class, then opened for edit and stored somewhere for consequent editing. Then, in order to create an output spreadsheet from edited document, a new [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) instance was created from another document. In such case, if new loaded document is not a spreadsheet, the feature with inserting a worksheet will not work (because there is no source, into which the worksheet can be inserted). Also this means that for such scenario the "output" source spreadsheet may not be the same document as the "original" source spreadsheet. For example, it is absolutely legal and working scenario, when user initially loads a spreadsheet named "A" into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class, edits (let's say) 2nd worksheet from it, then creates a new instance of the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class and loads another spreadsheet named "B" into it, and finally creates an output document from "B", where edited worksheet is injected on 5th position.
[SpreadsheetSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) class contains properties for protecting a worksheet from editing: [password](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/password) and [worksheet_protection](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetprotection). When these options are applied and inserting an edited worksheet into existing spreadsheet is applied too, the worksheet protection is applied only to the edited worksheet, which is inserting — all other worksheets are untouched.
```python
save_options = SpreadsheetSaveOptions(SpreadsheetFormats.XLSX)
save_options.worksheet_number = 1
save_options.insert_as_new_worksheet = True
save_options.password = "new password" # the output document will be encoded with this password
save_options.worksheet_protection = worksheet_protection # write-protection applied only to the inserted edited worksheet
```
---
## Product Overview
Path: /editor/python-net/product-overview/
GroupDocs.Editor for Python via .NET is a powerful and lightweight library which allows you to edit the most popular document formats using third-party front-end WYSIWYG HTML editors without any additional software.
GroupDocs.Editor supports most of the popular document formats such as PDF, DOCX, XLSX, PPTX, XPS and others.
By using GroupDocs.Editor for Python via .NET you can edit files of various formats and no third-party applications are required.
## How GroupDocs.Editor Works?
GroupDocs.Editor is a Python library (distributed as a self-contained wheel) designed to let you load documents of different formats, open them for editing, edit them, and save the edited version to some format, which may be exactly the same as the input, or may differ. GroupDocs.Editor is GUI-less — it has no graphical interface, only a public API — so it is used within an end-user environment rather than as a standalone application. Although GroupDocs.Editor can be used anywhere Python runs, it is intended to be used in web applications. The most common usage scenario implies that GroupDocs.Editor is used in a web application on the server side. Server-based code invokes GroupDocs.Editor methods, passes input documents and parameters, and obtains results that are transmitted to the client side into a WYSIWYG HTML editor, like CKEditor or TinyMCE. When the user has edited the document in the browser and sends the edited content back to the server, the server-based code again invokes GroupDocs.Editor, passes the edited content, and obtains the edited document in the desired format.
## Benefits of using GroupDocs.Editor
Using GroupDocs.Editor for Python via .NET in your project gives you the following benefits:
- Rich set of file editing features;
- Platform independence;
- Independence from third-party applications;
- Performance and scalability;
- Simple public API.
### Rich set of file editing features
GroupDocs.Editor for Python via .NET main features are the following:
- Translate a document to HTML/CSS markup with resources, compatible with HTML WYSIWYG editors;
- Save edited HTML/CSS back to the source document format;
- Export an edited document to PDF format;
- Plenty of additional options to customize the editing process — edit password-protected documents, extract document fonts, export document language information (useful for spell-checkers), specify document encoding or write-protection during saving, and more;
- Document information extraction — page count, size, encrypted flag, and so on.
### Platform Independence
GroupDocs.Editor for Python via .NET covers most of the popular development environments and deployment platforms. Its API can be used to develop applications for a wide range of operating systems, such as Windows, Linux, and macOS. Read ["System Requirements"]({{< ref "editor/python-net/getting-started/system-requirements" >}}) for more details.
The package is a self-contained wheel that works across Python 3.5 – 3.14 on Windows x64/x86, Linux x64, and macOS x64/ARM64.
### Independence from Other Applications
GroupDocs.Editor does not require third-party applications, for example Microsoft Office, to be installed on the machine in order to work. All GroupDocs components are completely independent. This makes GroupDocs.Editor a great alternative to automation in terms of security, stability, scalability/speed, price, and features for working with documents and related tasks.
### Performance and Scalability
We do care about performance. GroupDocs.Editor is designed to process thousands of files while utilizing as few resources as possible. We do performance testing to make sure there are no performance degradations from version to version.
GroupDocs.Editor is a single self-contained wheel that can be deployed with any Python application by simply installing it via `pip`. You do not need to worry about any other services or modules.
### Simple Public API
GroupDocs.Editor for Python via .NET public API was designed to be simple and intuitive. The methods do what you would expect from them and nothing more.
## Pricing and Policies
Please visit the ["Licensing and Subscription"]({{< ref "editor/python-net/getting-started/licensing-and-subscription.md" >}}) page for information on licenses and review the ["Pricing Information"](https://purchase.groupdocs.com/pricing/editor/family) page for details on pricing.
## Technical Support
We provide free and paid support for all of our users, including evaluation. For more information on GroupDocs.Editor technical support, please check the ["Technical Support"]({{< ref "editor/python-net/technical-support" >}}) page.
---
## Document protection
Path: /editor/python-net/document-protection/
The [`password`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingsaveoptions/) property, if set, enables document protection from opening by encrypting it with the specified password. However, almost all WordProcessing formats support a document protection from writing, which is completely different from opening. Document protection, like document encoding, also implies a password as a form of key, but it also supports different levels of protection: some of them allow only read-only mode, others allow to edit form-fields etc.
[**GroupDocs.Editor**](https://products.groupdocs.com/editor/python-net) allows to apply the document protection via the `protection` property in the [WordProcessingSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingsaveoptions) class. By default this property has a `None` value, which means that GroupDocs.Editor will not apply the protection to the document.
The `protection` property has a `WordProcessingProtection` type. This type is a class, which, in turn, consists of two properties: a password and a protection type.
The password is responsible for setting a password for the document protection. By default it is `None` — protection is not applied. If user wants to apply the document protection, he *needs* to assign some string to this property; otherwise protection will not be applied regardless from the value of the protection type property.
The protection type property determines the level of protection. By default it has a `NoProtection` value, which means that no protection will be applied. Other values are:
1. `AllowOnlyRevisions` — User can only add revision marks to the document.
2. `AllowOnlyComments` — User can only modify comments in the document.
3. `AllowOnlyFormFields` — User can only enter data in the form fields in the document.
4. `ReadOnly` — No changes are allowed to the document.
Take a note that both the password and the protection type are bound to each other. If user sets a valid non-`None`, not-empty password, but the protection type property has a `NoProtection` value, the protection will not be applied. And vice versa, if the protection type property has an `AllowOnlyFormFields` value, for example, but the password is `None` or an empty string, then the protection will not be applied either.
The snippet below illustrates how the `protection` property is assigned on the [WordProcessingSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingsaveoptions) instance before saving (the `WordProcessingProtection` and `WordProcessingProtectionType` types come from the `groupdocs.editor.options` module):
```python
from groupdocs.editor.formats import WordProcessingFormats
from groupdocs.editor.options import (
WordProcessingSaveOptions,
WordProcessingProtection,
WordProcessingProtectionType,
)
save_options = WordProcessingSaveOptions(WordProcessingFormats.DOCX)
# Make the resultant document read-only and protect this restriction with a password
save_options.protection = WordProcessingProtection(
WordProcessingProtectionType.READ_ONLY, "write_password")
```
## Complete example
Document protection (write protection) is separate from document encryption (protection from opening). The runnable example below shows the safe and most common scenario — loading the sample document, editing it, and saving the result encrypted with the [`password`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingsaveoptions/) property so that the output document can be opened only with this password.
{{< tabs "code-example-document-protection">}}
{{< tab "protect_document.py" >}}
```python
import os
from groupdocs.editor import Editor, EditableDocument, License
from groupdocs.editor.formats import WordProcessingFormats
from groupdocs.editor.options import WordProcessingSaveOptions
def protect_document():
# Optionally set a license
license_path = os.path.abspath("./GroupDocs.Editor.lic")
if os.path.exists(license_path):
License().set_license(license_path)
with Editor("./sample-document.docx") as editor:
# Open the document for editing
original = editor.edit()
# Edit the content (here done programmatically)
modified_content = original.get_embedded_html().replace("Title of the document", "Title of the protected document")
modified = EditableDocument.from_markup(modified_content)
# Encrypt the output document with a password (protection from opening)
save_options = WordProcessingSaveOptions(WordProcessingFormats.DOCX)
save_options.password = "p@ss"
editor.save(modified, "./protected-document.docx", save_options)
print("Saved a password-protected document")
original.dispose()
modified.dispose()
if __name__ == "__main__":
protect_document()
```
{{< /tab >}}
{{< tab "sample-document.docx" >}}
{{< tab-text >}}
`sample-document.docx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-word/document-protection/sample-document.docx) to download it.
{{< /tab-text >}}
{{< /tab >}}
{{< tab "protected-document.docx" >}}
```text
Binary file (DOCX, 53 KB)
```
[Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-word/document-protection/protect_document/protected-document.docx)
{{< /tab >}}
{{< /tabs >}}
---
## Generating worksheets (tabs) preview for spreadsheet
Path: /editor/python-net/generating-worksheets-preview-for-spreadsheet/
GroupDocs.Editor for Python via .NET allows to generate a preview for any worksheet (a.k.a. _tab_) in the spreadsheet (a.k.a. _workbook_) document in SVG format. With this feature the end-users are able to view and inspect the content of the spreadsheet without actually sending it for edit. This generated worksheet preview cannot be edited using the GroupDocs.Editor itself, but it can be saved and then viewed in any desktop or online image viewer as well as in the browser (because any modern browser actually supports viewing of SVG format).
This feature is working regardless of the licensing mode of the GroupDocs.Editor: it works the same for both trial and licensed mode, there are no trial limitations for this feature. While generating the worksheets preview, the GroupDocs.Editor doesn't write off the consumed bytes or credits.
Excel spreadsheets may have the so-called _hidden worksheets_ — GroupDocs.Editor generates an SVG preview for them too.
For generating the worksheets preview for a particular spreadsheet document the user must perform the next steps:
- Load a desired spreadsheet file to the [`Editor`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/) class.
- Call the [`get_document_info()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/getdocumentinfo/) method and specify a password of a loaded spreadsheet in case if this spreadsheet is protected with a password.
- In the obtained [`SpreadsheetDocumentInfo`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/spreadsheetdocumentinfo/) object invoke the `generate_preview(worksheet_index)` method and specify a zero-based _index_ (do not confuse with the worksheet _numbers_, which are 1-based) of the desired worksheet. If the specified index is lesser than 0 or exceeds the number of worksheets within a given spreadsheet, then an exception will be thrown.
- The `generate_preview(worksheet_index)` method returns a worksheet preview as an SVG vector image, that is encapsulated in the `SvgImage` class. This class has all necessary methods and properties to obtain the content of an SVG image in any desired form, save it to disk, stream and so on.
The snippet below illustrates opening an unprotected spreadsheet file, obtaining the number of all worksheets inside this spreadsheet, and then generating the previews for every worksheet in a loop. Then these previews are saved to the disk.
```python
import os
from groupdocs.editor import Editor
# Obtain a valid path to the spreadsheet file
input_path = "./sample-spreadsheet.xlsx"
output_folder = "./previews"
# Load spreadsheet file to the Editor constructor
with Editor(input_path) as editor:
# Get document info for this file
info_spreadsheet = editor.get_document_info()
# Get the number of all worksheets
worksheets_count = info_spreadsheet.page_count
# Iterate through all worksheets and generate the preview on every iteration
for worksheet_index in range(worksheets_count):
# Generate one preview as an SVG image by worksheet index
one_svg_preview = info_spreadsheet.generate_preview(worksheet_index)
# Save the SVG preview to a file
one_svg_preview.save(os.path.join(output_folder, one_svg_preview.filename_with_extension))
```
The worksheets preview feature is by its essence a method in the existing `SpreadsheetDocumentInfo` object, that obtains a worksheet index and returns an instance of the `SvgImage` class. If the end-user needs to obtain a preview of the worksheet in a raster format, but not in the vector, the `SvgImage` class also provides a method to convert the SVG content to the PNG format.
The complete example below loads a spreadsheet, opens its first worksheet for editing, and prints the length of the generated HTML content. This roundtrip confirms that the spreadsheet is read correctly before any preview is generated.
{{< tabs "code-example-generating-worksheet-preview-for-spreadsheet">}}
{{< tab "generating_worksheet_preview_for_spreadsheet.py" >}}
```python
import os
from groupdocs.editor import Editor, License
from groupdocs.editor.options import SpreadsheetEditOptions
def generating_worksheet_preview_for_spreadsheet():
# Optionally set a license
license_path = os.path.abspath("./GroupDocs.Editor.lic")
if os.path.exists(license_path):
License().set_license(license_path)
# Load the spreadsheet file to the Editor constructor
with Editor("./sample-spreadsheet.xlsx") as editor:
# Prepare edit options and select the 1st worksheet
edit_options = SpreadsheetEditOptions()
edit_options.worksheet_index = 0 # index is 0-based, so this is the 1st worksheet
# Open the worksheet for editing
worksheet = editor.edit(edit_options)
# Obtain the HTML content of the worksheet
content = worksheet.get_content()
print("Worksheet HTML content length:", len(content))
worksheet.dispose()
if __name__ == "__main__":
generating_worksheet_preview_for_spreadsheet()
```
{{< /tab >}}
{{< tab "sample-spreadsheet.xlsx" >}}
{{< tab-text >}}
`sample-spreadsheet.xlsx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-excel/generating-worksheet-preview-for-spreadsheet/sample-spreadsheet.xlsx) to download it.
{{< /tab-text >}}
{{< /tab >}}
{{< tab "generating-worksheet-preview-spreadsheet.txt" >}}
```text
Worksheet HTML content length: 42948
```
[Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-excel/generating-worksheet-preview-for-spreadsheet/generating_worksheet_preview_for_spreadsheet/generating-worksheet-preview-spreadsheet.txt)
{{< /tab >}}
{{< /tabs >}}
---
## Inserting edited slide into existing presentation
Path: /editor/python-net/inserting-edited-slide-into-existing-presentation/
By default the full presentation editing pipeline (cycle) is the next:
1. Load presentation in a form of a file or stream into constructor of the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class.
2. Select a specific slide to edit and specify its index in the [slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationeditoptions/slidenumber) property of [PresentationEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationeditoptions) class.
3. Open presentation for editing by calling [editor.edit()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method, passing [PresentationEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationeditoptions) instance with selected slide into it, and obtaining [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance from this method.
4. Emitting HTML and CSS markup, which represents a content of an edited document, from [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, using different methods like [get_content()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getcontent).
5. Passing this HTML and CSS markup into the WYSIWYG editor, which is located on client-side and is running in the browser.
6. End-user edits the document content in the WYSIWYG editor.
7. Edited document content, in form of HTML and CSS markup, is passed back to the server-side.
8. Then this content is passed into the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class, by using the [from_file](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/fromfile), [from_markup](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkup), or [from_markup_and_resource_folder](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkupandresourcefolder/) static methods.
9. Created [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, which holds an edited document content, is passed into the [editor.save](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method.
10. [editor.save](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method generates a new presentation, which contains only one single slide — those slide, that was edited on the client-side and which content was passed via [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class into this method.
The 10th step of described pipeline can be altered — edited slide can be inserted into original presentation, which was loaded on the 1st step.
[PresentationSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions) class contains two properties: integer [slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) and boolean flag [insert_as_new_slide](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide). Both of them have "usual" default values: [slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) has `0` and [insert_as_new_slide](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide) is set to `False`.
```python
save_options.slide_number = 0
save_options.insert_as_new_slide = False
```
By default, when these properties are not touched or at least [slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) has a default value `0`, GroupDocs.Editor will generate new single-slide presentation, as before. However, if [slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) property contains number, distinct from `0`, and valid presentation is loaded into [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class (it is expected to be the original presentation, which was edited, but it actually can be any presentation, even those, which has no relation to the original), then edited slide will be **inserted** into given presentation.
## slide_number property
[slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) property, if it is not zero, defines, *where*, at *which exact position* in the given presentation the new edited slide should be inserted. `insert_as_new_slide` parameter, which is a boolean flag, determines, *how* this slide should be inserted: should it *replace* the existing slide, that is located on specified position (`False`, default value), or it should be *injected between* existing slides, without rewriting them, and thus increasing the total amount of slides in the given presentation by one.
Because default `0` value of `slide_number` parameter is reserved, actual slide numbering starts from `1`. This is different from widely used 0-based indexing, however, makes sense, thus it is not an *index* of a slide, but rather *number* of a slide, the same as MS PowerPoint uses, for instance. This means that, for example, for given presentation, that consists of 5 slides, 1st one has a `1` slide number, and 5th — `5`. If user has specified a slide number, which exceeds the total amount of slides in presentation, this number will be automatically adjusted to the latest. This means that if, for example, for the same 5-slide presentation user will specify a `6`, `7` or even a very big value, it will be internally set a `5` — number of the latest slide.
Along with positive slide numbers, the `slide_number` property also supports negative numbering, which implies count from the end of presentation. In this case, `-1` is treated as the last slide, `-2` — the last but one, and so on. Again, like with positive numbering, in case when number exceeds the amount of slides, it will be adjusted to the closest — first slide for negative numbers.
Part of source code below explains this numbering system:
```python
save_options = PresentationSaveOptions(PresentationFormats.PPTX)
# let's say we have a presentation with 5 slides
save_options.slide_number = 0 # default value, given presentation will be ignored
# positive numbering
save_options.slide_number = 1 # first slide
save_options.slide_number = 2 # second slide
save_options.slide_number = 3 # third slide
save_options.slide_number = 4 # fourth slide
save_options.slide_number = 5 # fifth slide
save_options.slide_number = 6 # fifth slide, because value '6' exceeds the slides amount '5' and thus is adjusted to the closest
# negative numbering
save_options.slide_number = -1 # fifth slide, which is first from end (last)
save_options.slide_number = -2 # fourth slide, which is second from end (last but one)
save_options.slide_number = -3 # third slide, which is third from end
save_options.slide_number = -4 # second slide, which is fourth from end
save_options.slide_number = -5 # first slide, which is fifth from end
save_options.slide_number = -6 # first slide, because value '-6' exceeds the slides amount '5' and thus is adjusted to the closest
```
## insert_as_new_slide property
[insert_as_new_slide](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide) property complements the previously described [slide_number](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/slidenumber) property and is ignored, when `slide_number` is set to `0`. If `slide_number` points on the slide of specific number, [insert_as_new_slide](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide) determines how to treat this slide number and how to insert the slide:
* If [insert_as_new_slide](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide) property has default `False` value, the existing slide in given presentation will be completely erased, and the content of the new slide (which is located in the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance) will be putted on this place. As a result, presentation will preserve the same untouched amount of slides, but one of its slides (specified by the `slide_number`) will be replaced onto new one.
* If [insert_as_new_slide](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions/insertasnewslide) property has `True` value, the edited slide, obtained from [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, will be injected among existing slides in given presentation, so its amount of slides will be incremented by one. New slide is inserted at position, specified by `slide_number`, and all subsequent slides (following or preceding) will be shifted to the end or to the beginning accordingly, depending on positive or negative numbering.
Source code below shows, how slide number is treated when `insert_as_new_slide` property is enabled:
```python
save_options = PresentationSaveOptions(PresentationFormats.PPTX)
# let's say we have a presentation with 5 slides
save_options.slide_number = 0 # default value, given presentation will be ignored, as well as insert_as_new_slide
save_options.insert_as_new_slide = True # enabling the adding of slide instead of replacing
# positive numbering
save_options.slide_number = 1 # new slide is injected as first, while all following (including 'old' 1st) are shifting to the end
save_options.slide_number = 2 # new slide is injected as second, while 2nd, 3rd, 4th and 5th are shifting to the end
save_options.slide_number = 3 # new slide is injected as third, while 3rd, 4th and 5th are shifting to the end
save_options.slide_number = 4 # new slide is injected as fourth, while 4th and 5th are shifting to the end
save_options.slide_number = 5 # new slide is injected as fifth, while 5th is shifting to the end and becomes 6th
save_options.slide_number = 6 # new slide is injected as sixth, it already becomes the latest, none of existing slides are shifting to the end
save_options.slide_number = 7 # same as previous
# negative numbering
save_options.slide_number = -1 # new slide is injected as first from end (it becomes sixth if starting from beginning), none of existing slides are shifting to the end
save_options.slide_number = -2 # new slide is injected as second from end (it becomes fifth if starting from beginning), following single slide is shifting to the end
save_options.slide_number = -3 # new slide is injected as third from end (it becomes fourth if starting from beginning), two following slides are shifting to the end
save_options.slide_number = -4 # new slide is injected as fourth from end (it becomes third if starting from beginning), three following slides are shifting to the end
save_options.slide_number = -5 # new slide is injected as fifth from end (it becomes second if starting from beginning), four following slides are shifting to the end
save_options.slide_number = -6 # new slide is injected as sixth from end (it becomes first if starting from beginning), five following slides are shifting to the end
save_options.slide_number = -7 # same as previous
```
The complete example below loads a presentation, edits its first slide, and saves the result by inserting the edited slide into the original presentation as a new slide, keeping the original slides intact.
{{< tabs "code-example-inserting-edited-slide-into-existing-presentation">}}
{{< tab "inserting_edited_slide_into_existing_presentation.py" >}}
```python
import os
from groupdocs.editor import Editor, EditableDocument, License
from groupdocs.editor.options import PresentationLoadOptions, PresentationEditOptions, PresentationSaveOptions
from groupdocs.editor.formats import PresentationFormats
def inserting_edited_slide_into_existing_presentation():
# Optionally set a license
license_path = os.path.abspath("./GroupDocs.Editor.lic")
if os.path.exists(license_path):
License().set_license(license_path)
# Load input presentation to the Editor and specify loading options
with Editor("./sample-presentation.pptx", PresentationLoadOptions()) as editor:
# Prepare edit options and set the 1st slide to edit
edit_options = PresentationEditOptions()
edit_options.slide_number = 0 # index is 0-based, so this is the 1st slide
# Generate EditableDocument with the original content of the slide
slide_opened_for_edit = editor.edit(edit_options)
# Get the HTML-markup from the EditableDocument with the original content
original_html = slide_opened_for_edit.get_embedded_html()
# Emulate HTML content editing in a WYSIWYG-editor in the browser or somewhere else
edited_html = original_html.replace("", "
Edited content
") # Generate EditableDocument with the edited content slide_after_edit = EditableDocument.from_markup(edited_html) # Prepare save options that insert the edited slide into the original presentation save_options = PresentationSaveOptions(PresentationFormats.PPTX) save_options.slide_number = 1 # 1-based; insert at the 1st position save_options.insert_as_new_slide = True # keep the edited slide alongside the original ones # Save the presentation with the inserted edited slide editor.save(slide_after_edit, "./edited-presentation.pptx", save_options) slide_opened_for_edit.dispose() slide_after_edit.dispose() print("Saved presentation with the inserted edited slide to edited-presentation.pptx") if __name__ == "__main__": inserting_edited_slide_into_existing_presentation() ``` {{< /tab >}} {{< tab "sample-presentation.pptx" >}} {{< tab-text >}} `sample-presentation.pptx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-powerpoint/inserting-edited-slide-into-existing-presentation/sample-presentation.pptx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "edited-presentation.pptx" >}} ```text Binary file (PPTX, 232 KB) ``` [Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-powerpoint/inserting-edited-slide-into-existing-presentation/inserting_edited_slide_into_existing_presentation/edited-presentation.pptx) {{< /tab >}} {{< /tabs >}} ### Additional notes It is worth mentioning that the described feature doesn't modify the original presentation document, which was originally loaded into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class through its constructor. When saving a presentation and inserting the edited slide into existing presentation, the GroupDocs.Editor creates a full and exact copy of the original document, and only then adds or replaces the slide onto the edited. So the original document is not touched in any case. --- ## Introduction Path: /editor/python-net/introduction/ > This article explains the most common and fundamental principles of GroupDocs.Editor, how it works, what is its purpose, and how it should be used. [**GroupDocs.Editor**](https://products.groupdocs.com/editor/python-net) is a GUI-less class library, which means that it has only a programmatic interface (API). This fact means that in order to edit a document the user must use GroupDocs.Editor in conjunction with some 3rd-party editor application, through which GUI the end-user is able to edit document content. For GroupDocs.Editor it is not important which exactly editor software is used. But because GroupDocs.Editor is aimed at web-development, it has the only requirement — the 3rd-party editor should be compatible with HTML documents. In order to edit a document with GroupDocs.Editor, the user must perform several sequential steps: load the document into GroupDocs.Editor (using optional load options), open the document for editing (with optional edit options), generate HTML markup with resources (using different options and settings), and pass this markup to the 3rd-party WYSIWYG HTML-editor. Then the end-user edits the document content, and when he finishes editing and submits the edited document, this modified markup should be transferred back to GroupDocs.Editor and converted to the output document of the desired format. From the GroupDocs.Editor perspective, this pipeline can be conditionally divided into three main stages, that are described below. ## Loading document into the GroupDocs.Editor On the *[loading document]({{< ref "editor/python-net/developer-guide/load-document.md" >}})* stage the user should create an instance of the [`Editor` class](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) and pass an input document (through a file path or binary stream) along with document load options. Loading options are not required and GroupDocs.Editor can automatically detect the document format and select the most appropriate default options for the given format. But it is recommended to specify them explicitly. They are inevitable when trying to load password-protected documents. ```python from groupdocs.editor import Editor # Passing a path to the constructor; default WordProcessingLoadOptions will be applied automatically editor = Editor("document.docx") ``` After this stage the document is ready to be opened and edited. ## Opening a document for editing Because GroupDocs.Editor is a GUI-less library, a document cannot be edited directly within it. But in order to edit a document in a WYSIWYG HTML-editor, GroupDocs.Editor needs to generate an HTML-version of the document, because any WYSIWYG editor can work only with HTML/CSS markup. When an instance of the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class is created on the 1st stage, the user should open the document for editing by calling the [`edit()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method of the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class. This method returns an instance of the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class. This class can be described as a converted version of the input document, that is stored in an internal intermediate format, compatible with all formats that GroupDocs.Editor supports. With [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) the user can obtain HTML markup of the input document with different options, stylesheets, images, fonts, save an HTML-document to disk, and other things. It is implied that the HTML-markup, emitted by [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), is then passed into the client-side WYSIWYG HTML-editor, where the end-user can actually edit the document. Like with loading, the [`edit()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method accepts optional edit options, that control how exactly the document will be opened for editing. ```python from groupdocs.editor.options import WordProcessingEditOptions edit_options = WordProcessingEditOptions() edit_options.enable_language_information = True ready_to_edit = editor.edit(edit_options) ``` After this stage the document is ready to be passed to the WYSIWYG HTML-editor and its content can be edited by the end-user. ## Saving a document *[Saving a document]({{< ref "editor/python-net/developer-guide/save-document.md" >}})* is the final stage, which occurs when document content was edited in the WYSIWYG HTML-editor (or any other software, this makes no difference for GroupDocs.Editor) and should be saved back as a document of some format (like DOCX, PDF, or XLSX, for example). At this stage the user should create a new instance of the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class with HTML-markup and resources of the edited version of the original document, that was obtained from the end-user. The [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class contains several class methods, that allow to create its instances from HTML documents, which may be presented in different forms. And when an [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance is ready, it is possible to save it as an ordinary document using the [`save()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method of the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class. ```python from groupdocs.editor import EditableDocument from groupdocs.editor.formats import WordProcessingFormats from groupdocs.editor.options import WordProcessingSaveOptions after_edit = EditableDocument.from_markup("
") save_options = WordProcessingSaveOptions(WordProcessingFormats.RTF) editor.save(after_edit, "document.rtf", save_options) ``` Unlike the previous load options and edit options, save options are mandatory, because GroupDocs.Editor needs to know the exact document format for saving. ## Detecting document type Sometimes it is necessary to *[detect a document type and extract its metadata]({{< ref "editor/python-net/developer-guide/extracting-document-metainfo.md" >}})* before sending it for editing. For such scenarios GroupDocs.Editor allows to detect the document type and extract its most necessary metainfo depending on the document type: 1. Is the document encoded or not; 2. Exact document format; 3. Document size; 4. Number of pages (tabs); 5. Text encoding, if the document is textual. In order to detect the document type and gather its meta info, the user should load the desired document into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class and then call the [`get_document_info()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/getdocumentinfo) method. ## Describing options On every stage the user can adjust (tune) the processing by different options: 1. Load options for *loading* a document. 2. Edit options for opening a document for *editing*. 3. Save options for *saving* an edited document. Some of these options may be optional in specific cases, some are mandatory. For example, it is possible to load a document into the `Editor` class without load options — in such a case GroupDocs.Editor will try to detect the document format automatically and apply the most appropriate default options for the detected document format. ### Describing family formats All document formats, which GroupDocs.Editor supports, are grouped into family formats. Each family format has a lot of common features, so there are no options for each format — only for the family format. The relation between formats, family formats, import/export formats and options is illustrated in the table below. | Family format | Supported formats | Load | Save | Load options | Edit options | Save options | Metadata | | --- | --- | --- | --- | --- | --- | --- | --- | | WordProcessing | DOC, DOCX, DOCM, DOT, DOTX, DOTM, RTF, WordprocessingML Flat XML, ODT, OTT, Word 2003 XML |  |  | [WordProcessingLoadOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingloadoptions) | [WordProcessingEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingeditoptions) | [WordProcessingSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingsaveoptions) | [WordProcessingDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/wordprocessingdocumentinfo) | | Spreadsheet | XLS, XLT, XLSX, XLSM, XLSB, XLTX, XLTM, XLAM, SpreadsheetML XML, ODS, FODS, SXC, DIF |  |  | [SpreadsheetLoadOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetloadoptions) | [SpreadsheetEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheeteditoptions) | [SpreadsheetSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) | [SpreadsheetDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/spreadsheetdocumentinfo) | | DSV | CSV, TSV, semicolon-separated, whitespace-separated, arbitrary separator |  |  | N/A | [DelimitedTextEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/delimitedtexteditoptions) | [DelimitedTextSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/delimitedtextsaveoptions) | N/A | | Presentation | PPT, PPTX, PPTM, PPS, PPSX, PPSM, POT, POTX, POTM, ODP, OTP |  |  | [PresentationLoadOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationloadoptions) | [PresentationEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationeditoptions) | [PresentationSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/presentationsaveoptions) | [PresentationDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/presentationdocumentinfo) | | XML | Any XML document |  |  | N/A | [XmlEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/xmleditoptions) | N/A | [TextualDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/textualdocumentinfo) | | TXT | Any text document |  |  | N/A | [TextEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/texteditoptions) | [TextSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/textsaveoptions) | [TextualDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/textualdocumentinfo) | | Fixed-layout format | PDF |  |  | [PdfLoadOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/pdfloadoptions) | [PdfEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/pdfeditoptions) | [PdfSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/pdfsaveoptions) | [FixedLayoutDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/fixedlayoutdocumentinfo) | | Fixed-layout format | XPS (including OpenXPS) |  |  | N/A | N/A | [XpsSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/xpssaveoptions) | [FixedLayoutDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/fixedlayoutdocumentinfo) | | e-Book | Mobi, AZW3, ePub |  |  | N/A | [EbookEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/ebookeditoptions) | [EbookSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/ebooksaveoptions) | [EbookDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/ebookdocumentinfo) | | Email | EML, EMLX, TNEF, MSG, HTML, MHTML, ICS, VCF, PST, MBOX, OFT |  |  | N/A | [EmailEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/emaileditoptions) | [EmailSaveOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/emailsaveoptions) | [EmailDocumentInfo](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.metadata/emaildocumentinfo) | ### Additional materials Detailed information about every stage of document processing along with source code examples, options explanations and so on, can be found in the next articles: 1. [Load document]({{< ref "editor/python-net/developer-guide/load-document.md" >}}) 2. [Save document]({{< ref "editor/python-net/developer-guide/save-document.md" >}}) Detailed reviews of the supported family formats and the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class, along with source code examples, can be found in the next articles: * [Create and edit new WordProcessing document]({{< ref "editor/python-net/developer-guide/create-document.md" >}}) * [Extracting document metainfo]({{< ref "editor/python-net/developer-guide/extracting-document-metainfo.md" >}}) * [Working with formats]({{< ref "editor/python-net/developer-guide/working-with-formats.md" >}}) * [Working with HTML resources]({{< ref "editor/python-net/developer-guide/working-with-html-resources.md" >}}) * [How to edit Mobi file]({{< ref "editor/python-net/developer-guide/working-with-mobi-documents.md" >}}) --- ## Save HTML to folder Path: /editor/python-net/save-html-to-folder/ > This demonstration shows how to open an input document, convert it to an intermediate [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), and save it to disk as an HTML file with a resource folder. Almost all HTML WYSIWYG client-side editors are able to open an HTML document from disk (from a path). [**GroupDocs.Editor**](https://products.groupdocs.com/editor/python-net) allows to open any supportable document, convert it to HTML and save it to disk, which may be very useful for subsequently editing it in some WYSIWYG editor. When the document is opened for editing with [`editor.edit()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit), the resulting [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) can be written to disk with its [`save()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/save) method. The method accepts the path to the output HTML file and the path to a folder where the resources (images, stylesheets, fonts) will be saved. ```python from groupdocs.editor import Editor from groupdocs.editor.options import WordProcessingLoadOptions load_options = WordProcessingLoadOptions() editor = Editor("document.docx", load_options) # passing path and load options to the constructor document = editor.edit() # Save HTML markup together with resources into a folder document.save("document.html", "document_resources") ``` In this example we load an input WordProcessing (DOCX) document into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class with load options, specific for this document family type - [`WordProcessingLoadOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingloadoptions). Then the document is converted to the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) using the [`edit()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method. In the last line the content is saved to the HTML file on disk, and all resources are placed into the specified folder. ## Complete code example The example below loads a document, opens it for editing, and saves the HTML markup together with all of its resources into a folder. {{< tabs "code-example-save-html-to-folder">}} {{< tab "save_html_to_folder.py" >}} ```python import os from groupdocs.editor import Editor, License def save_html_to_folder(): # Optionally set a license license_path = os.path.abspath("./GroupDocs.Editor.lic") if os.path.exists(license_path): License().set_license(license_path) with Editor("./sample-document.docx") as editor: editable = editor.edit() # Write the HTML markup and every resource into a folder editable.save("output.html", "output_resources") editable.dispose() print("Saved HTML to output.html with resources in output_resources") if __name__ == "__main__": save_html_to_folder() ``` {{< /tab >}} {{< tab "sample-document.docx" >}} {{< tab-text >}} `sample-document.docx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/editabledocument/save-html-to-folder/sample-document.docx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.html" >}} ```text
", "
Edited content
") # Generate EditableDocument with the edited content slide_after_edit = EditableDocument.from_markup(edited_html) # Prepare save options that delete a slide during saving save_options = PresentationSaveOptions(PresentationFormats.PPTX) # A non-zero slide_number is required so the presentation is rebuilt and deletions are applied save_options.slide_number = 1 # 1-based; insert the edited slide at the 1st position save_options.insert_as_new_slide = True # keep the edited slide alongside the original ones save_options.slide_numbers_to_delete = [1] # delete the 1st original slide (1-based) # Save the presentation with the deleted slide editor.save(slide_after_edit, "./edited-presentation.pptx", save_options) slide_opened_for_edit.dispose() slide_after_edit.dispose() print("Saved presentation with a deleted slide to edited-presentation.pptx") if __name__ == "__main__": deleting_slides_from_presentation() ``` {{< /tab >}} {{< tab "sample-presentation.pptx" >}} {{< tab-text >}} `sample-presentation.pptx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-powerpoint/deleting-slides-from-presentation/sample-presentation.pptx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "edited-presentation.pptx" >}} ```text Binary file (PPTX, 144 KB) ``` [Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-powerpoint/deleting-slides-from-presentation/deleting_slides_from_presentation/edited-presentation.pptx) {{< /tab >}} {{< /tabs >}} --- ## Deleting worksheets from spreadsheet Path: /editor/python-net/deleting-worksheets-from-spreadsheet/ A **spreadsheet**, also known as a **workbook** — is a [family of the document formats](https://docs.fileformat.com/spreadsheet/), designed to work with tabular data. [XLS](https://docs.fileformat.com/spreadsheet/xls/), [XLSX](https://docs.fileformat.com/spreadsheet/xlsx/), [ODS](https://docs.fileformat.com/spreadsheet/ods/), [CSV](https://docs.fileformat.com/spreadsheet/csv/) formats are the most common examples of such document formats, while Microsoft Excel, LibreOffice Calc, Apache OpenOffice Calc are examples of table processors — programs, which allow the creation and editing of such documents. GroupDocs.Editor has an ability to edit existing spreadsheet documents, to create new spreadsheets from scratch, and also to delete worksheets from the edited spreadsheet during saving. Need to keep in mind that not every spreadsheet may have the worksheets. For example, text-based separator-delimited formats like [CSV](https://docs.fileformat.com/spreadsheet/csv/) and [TSV](https://docs.fileformat.com/spreadsheet/tsv/) are basically the text files, they have no worksheets, so the worksheet cannot be removed from them. But almost all of the binary spreadsheet formats like XLS, XLSX and ODS do have them. In GroupDocs.Editor the user edits one worksheet at a time — the spreadsheet [is loaded](https://docs.groupdocs.com/editor/python-net/load-document/) to the constructor of the [`Editor`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class, then worksheet is specified by its number in the [`SpreadsheetEditOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheeteditoptions), and then document is converted to the editable form, represented by the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class. Straight and simple. However, when the content of the worksheet was edited by the user and passed back to the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class, there are two options: 1. Users can save the edited content of the worksheet as a new spreadsheet with this single worksheet inside. This is the default behaviour that was present in the GroupDocs.Editor from its beginning. 2. Users can save the edited worksheet by inserting it to the original spreadsheet. This is described in a [separate article](https://docs.groupdocs.com/editor/python-net/inserting-edited-worksheet-into-existing-spreadsheet/). This second option also has two sub-options: 1. The edited worksheet can *replace* the original worksheet in the input spreadsheet. For instance, in the loaded spreadsheet with 3 worksheets the user has chosen the 2nd one for edit; this worksheet was edited and then inserted back to the input spreadsheet, replacing the original 2nd worksheet onto the edited one. 2. The edited worksheet can be inserted into the input spreadsheet to stay *together* with the original one. For instance, in the loaded spreadsheet with 3 worksheets the user has chosen the 2nd one for edit; this worksheet was edited and then inserted back to the input spreadsheet, so now the spreadsheet contains 4 worksheets: the first, fourth, and two versions of the second (original and edited). When the 2nd option (inserting edited worksheet into the original spreadsheet) is used, users also can delete particular worksheet(s) from it. The [`SpreadsheetSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) class has a [`worksheet_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumberstodelete/) property of `list[int]` type. By default it has a `None` value, which means that no worksheets should be removed. However, when it has one or more valid worksheet numbers, the worksheets with these numbers are removed while calling the [`editor.save()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method. Need to mention that the worksheet numbers in a [`worksheet_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumberstodelete/) property are 1-based. For instance, for removing the first and fourth worksheets the [`worksheet_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumberstodelete/) property should have a `[1, 4]` value. With the worksheet removal feature the GroupDocs.Editor performs the next algorithm during saving the spreadsheet: 1. User edits a content of the worksheet in the WYSIWYG-editor, passes the edited content to the instance of the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class [using one of its static methods](https://docs.groupdocs.com/editor/python-net/create-editabledocument-from-file-or-markup/), creates and adjusts the [`SpreadsheetSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions), and passes the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, [`SpreadsheetSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) instance, and output stream or file path for writing to the [`editor.save()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method. 2. If the value of the [`worksheet_number`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property in the [`SpreadsheetSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) instance is set to the default value `0`, the GroupDocs.Editor generates a new spreadsheet with one worksheet. The [`worksheet_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumberstodelete/) property is ignored regardless of its value. 3. If the value of the [`worksheet_number`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property in the [`SpreadsheetSaveOptions`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions) instance has some non-zero value, the GroupDocs.Editor treats it as a command to insert the edited worksheet into the original spreadsheet. 4. Depending on the [`insert_as_new_worksheet`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/insertasnewworksheet/) property, the GroupDocs.Editor replaces old worksheet, specified by the value of the [`worksheet_number`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumber) property, on the new one, taken from the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, or puts the new worksheet to be placed among existing worksheets without replacing any of them. 5. Finally, when all worksheet insertions and rearrangements are finished, the GroupDocs.Editor reads the value of the [`worksheet_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumberstodelete/) property, iterates over all numbers and removes specified worksheets from the spreadsheet. 6. The final spreadsheet, with updated and deleted worksheets, is written to the output stream or file. The example below shows a full roundtrip of an input XLSX file: a spreadsheet is loaded, its first worksheet is converted to the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument), then HTML-markup is emitted from the [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, modified, and the edited HTML-markup is converted back to another [`EditableDocument`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance. This edited worksheet is then inserted into the original spreadsheet, while the [`worksheet_numbers_to_delete`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/spreadsheetsaveoptions/worksheetnumberstodelete/) property is set to remove the original second worksheet during saving. {{< tabs "code-example-deleting-worksheets-from-spreadsheet">}} {{< tab "deleting_worksheets_from_spreadsheet.py" >}} ```python import os from groupdocs.editor import Editor, EditableDocument, License from groupdocs.editor.options import SpreadsheetLoadOptions, SpreadsheetEditOptions, SpreadsheetSaveOptions from groupdocs.editor.formats import SpreadsheetFormats def deleting_worksheets_from_spreadsheet(): # Optionally set a license license_path = os.path.abspath("./GroupDocs.Editor.lic") if os.path.exists(license_path): License().set_license(license_path) # Load input spreadsheet to the Editor and specify loading options with Editor("./sample-spreadsheet.xlsx", SpreadsheetLoadOptions()) as editor: # Prepare edit options and set the 1st worksheet to edit edit_options = SpreadsheetEditOptions() edit_options.worksheet_index = 0 # index is 0-based, so this is the 1st worksheet # Generate EditableDocument with the original content of the 1st worksheet worksheet_opened_for_edit = editor.edit(edit_options) # Get the HTML-markup from the EditableDocument with the original content original_html = worksheet_opened_for_edit.get_embedded_html() # Emulate HTML content editing in a WYSIWYG-editor in the browser or somewhere else edited_html = original_html.replace("", "
Edited content
") # Generate EditableDocument with the edited content worksheet_after_edit = EditableDocument.from_markup(edited_html) # Prepare save options that delete a worksheet during saving save_options = SpreadsheetSaveOptions(SpreadsheetFormats.XLSX) # A non-zero worksheet_number is required so the spreadsheet is rebuilt and deletions are applied save_options.worksheet_number = 1 # 1-based; insert the edited worksheet at the 1st position save_options.insert_as_new_worksheet = True # keep the edited worksheet alongside the original ones save_options.worksheet_numbers_to_delete = [1] # delete the 1st original worksheet (1-based) # Save the spreadsheet with the deleted worksheet editor.save(worksheet_after_edit, "./edited-spreadsheet.xlsx", save_options) worksheet_opened_for_edit.dispose() worksheet_after_edit.dispose() print("Saved spreadsheet with a deleted worksheet to edited-spreadsheet.xlsx") if __name__ == "__main__": deleting_worksheets_from_spreadsheet() ``` {{< /tab >}} {{< tab "sample-spreadsheet.xlsx" >}} {{< tab-text >}} `sample-spreadsheet.xlsx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-excel/deleting-worksheets-from-spreadsheet/sample-spreadsheet.xlsx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "edited-spreadsheet.xlsx" >}} ```text Binary file (XLSX, 35 KB) ``` [Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-excel/deleting-worksheets-from-spreadsheet/deleting_worksheets_from_spreadsheet/edited-spreadsheet.xlsx) {{< /tab >}} {{< /tabs >}} --- ## Enabling language information Path: /editor/python-net/enabling-language-information/ Documents of all WordProcessing formats can contain text in different languages. But, unlike the plain text documents (TXT), WordProcessing documents also contain a metadata about specific language (locale) of every piece of text. [**GroupDocs.Editor**](https://products.groupdocs.com/editor/python-net) allows to extract and export this language information. For achieving this the [WordProcessingEditOptions](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingeditoptions) class contains the [`enable_language_information`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingeditoptions/) public boolean property: ```python from groupdocs.editor.options import WordProcessingEditOptions edit_options = WordProcessingEditOptions() edit_options.enable_language_information = True ``` By default its value is `False`, which means that language metadata will not be extracted. But when this option is manually enabled, GroupDocs.Editor extracts locale info for every piece of textual content and preserves it in the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, when document is edited. Finally, when user has obtained the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance and is generating the HTML markup for transferring it to the WYSIWYG HTML-editor in order to make document editable in the browser, this language information is represented as the 'lang' HTML attributes with appropriate values inside the SPAN HTML elements. Enabling language information is useful when document contains different text parts in different languages; if document has text in some single language, this option does not have much sense and thus is disabled by default. However, when document is multi-language, enabling language information may be very suitable for two scenarios: * It eases spell checking for client-side JavaScript spell-checkers, that are working in the browser. However, this is very dependent on specific spell-checker, as not all spell-checkers are able to grab values from "lang" attributes or even use language information at all. * It improves the quality of output WordProcessing document in roundtrip scenarios. When a document with enabled [`enable_language_information`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor.options/wordprocessingeditoptions/) option was converted to the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, then HTML markup was generated, edited in some HTML-editor, and then a new instance of [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class was created from the edited markup, language metadata in "lang" attributes is still preserved. When the edited [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) will be converted back to the output document of some WordProcessing format like DOCX or RTF, the textual content inside it will have connections to the correct locale. ## Complete example The example below loads the sample document, edits it with `enable_language_information` turned on, and prints the length of the generated HTML markup that carries the language metadata. {{< tabs "code-example-enabling-language-information">}} {{< tab "enable_language_information.py" >}} ```python import os from groupdocs.editor import Editor, License from groupdocs.editor.options import WordProcessingEditOptions def enable_language_information(): # Optionally set a license license_path = os.path.abspath("./GroupDocs.Editor.lic") if os.path.exists(license_path): License().set_license(license_path) # Enable extraction of language (locale) information edit_options = WordProcessingEditOptions() edit_options.enable_language_information = True with Editor("./sample-document.docx") as editor: editable = editor.edit(edit_options) html = editable.get_content() print("HTML markup with language information, length:", len(html)) editable.dispose() if __name__ == "__main__": enable_language_information() ``` {{< /tab >}} {{< tab "sample-document.docx" >}} {{< tab-text >}} `sample-document.docx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/edit-document/edit-word/enabling-language-information/sample-document.docx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "enable-language-information.txt" >}} ```text HTML markup with language information, length: 35801 ``` [Download full output](/editor/python-net/_output_files/developer-guide/edit-document/edit-word/enabling-language-information/enable_language_information/enable-language-information.txt) {{< /tab >}} {{< /tabs >}} --- ## Showcases Path: /editor/python-net/showcases/ {{< alert style="info" >}}Want to try GroupDocs.Editor for Python via .NET by yourself? Please check the Python code examples, how-to articles, and free online demonstrations provided below to learn more about the document editing features.{{< /alert >}} ## GitHub Examples To get started with GroupDocs.Editor for Python via .NET, you can explore a variety of runnable code examples available on GitHub. The repository provides a rich collection of resources that showcase the capabilities of the library and demonstrate how to implement document editing features. You can access these resources at the following GitHub repository: [GroupDocs.Editor for Python via .NET GitHub Repository](https://github.com/groupdocs-editor/GroupDocs.Editor-for-Python-via-.NET) There you'll find code examples that cover different scenarios, helping you understand the library's usage and integration possibilities. See [How to Run Examples]({{< ref "editor/python-net/getting-started/how-to-run-examples.md" >}}) to run them locally. ## Online Demo To experience the full potential of GroupDocs.Editor, you can take advantage of the free online document editor app. This online demo allows you to edit various document formats, including DOCX, PDF, XLSX, PPTX, and more. It's a great way to explore the capabilities of GroupDocs.Editor without any installation. Visit the following link to access the Free Online Document Editor App: [Free Online Document Editor App](https://products.groupdocs.app/editor) By using the online demo, you can test and evaluate the features of GroupDocs.Editor in a real-world editing environment. It's a convenient way to assess the library's capabilities and determine its suitability for your document editing needs. --- ## System Requirements Path: /editor/python-net/system-requirements/ ## Overview GroupDocs.Editor for Python via .NET does not require MS Office, Open Office, or any other external software or third-party tool to be installed. The package is a self-contained wheel that bundles everything it needs, so the only prerequisites are a supported version of Python and the operating-system packages listed below. Just follow one of the ways described in [Development Environment, Installation and Configuration]({{< ref "editor/python-net/getting-started/installation.md" >}}). ## Supported Python Versions GroupDocs.Editor for Python via .NET supports the following Python versions: * Python 3.5 * Python 3.6 * Python 3.7 * Python 3.8 * Python 3.9 * Python 3.10 * Python 3.11 * Python 3.12 * Python 3.13 * Python 3.14 ## Supported Operating Systems The package is distributed as a self-contained wheel that runs on the following platforms: ### Windows * Windows x64 * Windows x86 No additional dependencies are required on Windows. ### Linux * Linux x64 On Linux you need to install a few system packages for graphics and font rendering: ```bash apt install libgdiplus libfontconfig1 ttf-mscorefonts-installer ``` ### macOS * macOS x64 (Intel) * macOS ARM64 (Apple Silicon) On macOS install the graphics library via Homebrew: ```bash brew install mono-libgdiplus ``` ## No MS Office Required Unlike many document-processing tools, GroupDocs.Editor for Python via .NET does not depend on Microsoft Office, Open Office, or any other application being installed on the machine. All loading, editing, and saving of Word Processing documents, Spreadsheets, Presentations, PDFs, emails, eBooks, and text/markup formats is performed entirely by the bundled engine. --- ## Working with resources Path: /editor/python-net/working-with-resources/ > This demonstration shows and explains different operations with resources, including retrieving them in different scenarios. ## Introduction Almost all documents of any type have resources. These are first of all images; some document formats also hold fonts. Even for a plain text document (TXT), when converting it to HTML for editing, there will be one stylesheet, that is treated as a resource. WordProcessing documents of some formats, Office Open XML usually, can also contain embedded audio files. GroupDocs.Editor allows to work with resources on the editing phase, when the document was loaded into the [Editor](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor) class and opened for editing by generating the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance, that is produced by the [Editor.edit()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method. GroupDocs.Editor classifies all resources into several groups: * Images, including: raster (PNG, BMP, JPEG, GIF, ICON) and vector (SVG and WMF). * Fonts, including: TTF, EOT, WOFF, WOFF2. * Textual resources: CSS stylesheets. * Audio files: MP3. ## Preparations Let's prepare an [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance by loading and editing some input WordProcessing document, as always: ```python from groupdocs.editor import Editor from groupdocs.editor.options import WordProcessingLoadOptions editor = Editor("document.docx", WordProcessingLoadOptions()) before_edit = editor.edit() # create an EditableDocument instance ``` ## Obtaining resources Now, when the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance is ready, it is possible to obtain resources from it, and [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) provides several ways for this. First of all, resources can be retrieved by their type. [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) exposes an iterable collection for every resource type: * `images` — all images, raster and vector. * `fonts` — all fonts. * `css` — the CSS stylesheets, where each item represents one stylesheet. * `audio` — the MP3 audio files. Secondly, completely all resources may be obtained with a single property — `all_resources`. It returns everything above, combined, and in fact is a concatenation of the previous collections. All these collections can be iterated with `for` loops and measured with `len()`: ```python images = before_edit.images fonts = before_edit.fonts stylesheets = before_edit.css audio_files = before_edit.audio all_together = before_edit.all_resources for one_image in images: print("image resource:", one_image) ``` ## CSS resources There is also a dedicated way for the stylesheets. The reason is that stylesheets can contain external resources too, presented as links with URLs — for example images, fonts, and other stylesheets. In such a case it may be necessary to adjust such a link. For coping with this, [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) contains the [`get_css_content()`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/getcsscontent) method. Without arguments it returns the stylesheets as-is; it can also accept prefixes for external images and fonts referenced from the stylesheets: ```python stylesheets_without_prefixes = before_edit.get_css_content() external_images_prefix = "http://www.mywebsite.com/images/id=" external_fonts_prefix = "http://www.mywebsite.com/fonts/id=" stylesheets_with_prefixes = before_edit.get_css_content(external_images_prefix, external_fonts_prefix) ``` ## Complete code example The example below loads a document, opens it for editing, and reports how many resources of each kind were extracted. {{< tabs "code-example-working-with-resources">}} {{< tab "working_with_resources.py" >}} ```python import os from groupdocs.editor import Editor, License def working_with_resources(): # Optionally set a license license_path = os.path.abspath("./GroupDocs.Editor.lic") if os.path.exists(license_path): License().set_license(license_path) with Editor("./sample-document.docx") as editor: before_edit = editor.edit() # Inspect the extracted resources by their type print("Images:", len(before_edit.images)) print("Stylesheets:", len(before_edit.css)) print("Fonts:", len(before_edit.fonts)) print("All resources:", len(before_edit.all_resources)) # Enumerate the stylesheets of the document for one_stylesheet in before_edit.css: print("stylesheet resource:", one_stylesheet) before_edit.dispose() if __name__ == "__main__": working_with_resources() ``` {{< /tab >}} {{< tab "sample-document.docx" >}} {{< tab-text >}} `sample-document.docx` is the sample file used in this example. Click [here](/editor/python-net/_sample_files/developer-guide/editabledocument/working-with-resources/sample-document.docx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "working-with-resources.txt" >}} ```text Images: 4 Stylesheets: 1 Fonts: 0 All resources: 5 stylesheet resource: GroupDocs.Editor.HtmlCss.Resources.Textual.CssText ``` [Download full output](/editor/python-net/_output_files/developer-guide/editabledocument/working-with-resources/working_with_resources/working-with-resources.txt) {{< /tab >}} {{< /tabs >}} --- ## Create EditableDocument from file or markup Path: /editor/python-net/create-editabledocument-from-file-or-markup/ > This demonstration shows how to create an instance of the EditableDocument class from HTML files on disk or from HTML markup with resources. ## Introduction When working with [**GroupDocs.Editor**](https://products.groupdocs.com/editor/python-net) in the usual way by loading, opening, editing and saving documents, the instances of the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class are produced by the [Editor.edit()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/edit) method and accepted by the [Editor.save()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method. However, in some cases it is required to create an [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance from existing HTML markup with optional resources. For example, some document was loaded to the `Editor` class, opened for editing, and then the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) was saved to the disk as a set of an HTML file and connected resources. Then this HTML document was passed to the WYSIWYG-editor, edited, and saved back to the disk as modified HTML. Or the raw output from the WYSIWYG-editor was saved to a string variable. In order to save it to some final format like DOCX or XLSX, the user needs to pass the document to the [Editor.save()](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editor/save) method in the form of an [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance. This means that the user should create an instance of the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) class manually. [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) contains three public class methods for creating its instances: [`from_file`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/fromfile), [`from_markup`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkup) and [`from_markup_and_resource_folder`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkupandresourcefolder). This article reviews all of them. ## Opening from file Let's suppose that we have an HTML file with edited document content, that is saved on the disk. There is also a folder with resources (images, fonts, stylesheets), that are used by this document, and the document has correct links to these resources. Let's say the HTML document has the name "document.html". The resource folder is located near it and has the name "document_resources", and, what is most important, the HTML markup from "document.html" has proper links to files from the "document_resources" folder. In that case creating the [EditableDocument](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument) instance is the most simple — the [`from_file`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/fromfile) class method accepts the path to the HTML file and the path to the resource folder: ```python from groupdocs.editor import EditableDocument # HTML file plus the folder where its resources are stored document = EditableDocument.from_file("document.html", "document_resources") ``` If the HTML file contains correct links, GroupDocs.Editor will scan the links and find the resources automatically. If the HTML file contains a link to a resource that is not present in the resource folder, it will be omitted. ## Opening from markup and prepared resources In some cases the edited HTML document is not present as a file. It may be stored in a database, obtained from remote storage, or something else. Quite often the whole document content, with HTML- and CSS-markup and all the resources, is packed inside a single string (resources are packed into the HTML markup using the [data URI scheme](https://en.wikipedia.org/wiki/Data_URI_scheme) with base64 encoding). In such cases, when there are no external resources, the [`from_markup`](https://reference.groupdocs.com/editor/python-net/groupdocs.editor/editabledocument/frommarkup) class method is the most convenient — it accepts a single string with the raw HTML markup: ```python from groupdocs.editor import EditableDocument input_html_markup = "