Groupdocs Editor with PPTX

Hello Team,

I am working with Editor in my application, I took help from Groupdocs team for Editor with DOCX file and I got the code from the team, Code below.

[HttpPost]
[Route("api/GroupDocsApi/CreateEmptyWordFile")]
public async Task<SPResponse> CreateEmptyWordFile(DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest request)
{
    try
    {
        Document document = new Document();
        await Task.Run(() => document.Save(request.FileUploadPath));
        return new SPResponse() { ReturnStatus = "0" };
    }
    catch (Exception ex)
    {
        return new SPResponse() { ReturnStatus = "-1", ReturnMessage = ex.Message };
    }
}


[HttpPost]
[Route("api/GroupDocsApi/LoadWordFile")]
public async Task<SPResponse> LoadWordFile(DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest request)
{
    try
    {
        SPResponse sp = new SPResponse() { ReturnStatus = "-1" };

        using (Editor editor = new Editor(request.FileUploadPath))
        {
            WordProcessingEditOptions options = new WordProcessingEditOptions();
            EditableDocument editableDocument = editor.Edit(options);

            string htmlWithStyles = editableDocument.GetEmbeddedHtml();
            sp.Ref1 = htmlWithStyles;
            sp.ReturnStatus = "0";
        }
        return sp;
    }
    catch (Exception ex)
    {
        return new SPResponse() { ReturnStatus = "-1", ReturnMessage = ex.Message };
    }
}

[HttpPost]
[Route("api/GroupDocsApi/SaveWordFile")]
public SPResponse SaveWordFile(DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest request)
{
    SPResponse sp = new SPResponse() { ReturnStatus = "-1" };
    string editedHtmlContent = request.EditorData;
            
    string inputFilePath = Path.Combine(request.StoragePath, request.GUID);
    string guid = Guid.NewGuid().ToString();
    string extension = Path.GetExtension(request.GUID);
    string outputFilePath = Path.Combine(request.StoragePath, guid + "." + extension);

    EditableDocument afterEdit = EditableDocument.FromMarkup($"<body>{editedHtmlContent}</body>", new List<IHtmlResource>());
    GroupDocs.Editor.Options.WordProcessingSaveOptions saveOptions = new GroupDocs.Editor.Options.WordProcessingSaveOptions();
    try
    {
        using (Editor editor = new Editor(inputFilePath))
        {
            editor.Save(afterEdit, outputFilePath, saveOptions);
        }
        sp.ReturnStatus = "0";
        sp.Ref1 = guid;
    }
    catch (Exception ex)
    {

        throw;
    }
    return sp;
}

Front-End

@model DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest
@{
    ViewBag.Title = "Create/Edit Office Files";
    Layout = "~/Views/Shared/_Layout.cshtml";
    ViewBag.Description = "(" + Model.FileEx + ")";
}

@Html.HiddenFor(m => m.FileServerId)
@Html.HiddenFor(m => m.FileId)
@Html.Hidden("hdnGroupDocsWebApiURL", (string)ViewBag.GroupDocsWebApiURL)

<h2>@Html.DisplayFor(a => a.FileName)</h2>
<div class="row">
    <div class="col-3">
        <div id="gd-thumbnails-panzoom" style="overflow: auto; max-height: 600px;">
        </div>
    </div>
    <div class="col-9">
        <form id="OfficeFilesCreatorForm">
            <div id="dv_Editor">
                <textarea id="editor" rows="4" cols="32" style="height:600px;">
                    @Html.Raw(Model.HtmlContent)
                </textarea>
                <br />
                <button id="btnSave" class="btn btn-primary">Save</button>
            </div>
        </form>
    </div>
</div>
<script>
    $("#editor").kendoEditor({
        tools: [
            "bold",
            "italic",
            "underline",
            "strikethrough",
            "justifyLeft",
            "justifyCenter",
            "justifyRight",
            "justifyFull",
            "insertUnorderedList",
            "insertOrderedList",
            "indent",
            "outdent",
            "createLink",
            "unlink",
            "insertImage",
            "subscript",
            "superscript",
            "tableWizard",
            "addRowAbove",
            "addRowBelow",
            "addColumnLeft",
            "addColumnRight",
            "deleteRow",
            "deleteColumn",
            "mergeCellsHorizontally",
            "mergeCellsVertically",
            "splitCellHorizontally",
            "splitCellVertically",
            "viewHtml",
            "formatting",
            "cleanFormatting",
            "copyFormat",
            "applyFormat",
            /*"fontName",*/
            "fontSize",
            "foreColor",
            "backColor",
            "pdf",
            {
                name: "fontName",
                items: [
                    { text: "Andale Mono", value: "Andale Mono" },
                    { text: "Arial", value: "Arial" },
                ]
            },
            {
                name: "custom",
                tooltip: "Add page break",
                exec: function (e) {
                    var editor = $(this).data("kendoEditor");
                    editor.exec("inserthtml", { value: "<p STYLE='page-break-before: always'>Page break here!</p>" });
                }
            }
        ],
        pdf: {
            paperSize: 'A5',
        }
    });

    kendo.pdf.defineFont({
        "TSCu_SaiIndira": "../../Content/kendo/2019.2.514/fonts/TSCu_SaiIndira.ttf",
        "Padasalai-TAU-Marutham": "../../Content/kendo/2019.2.514/fonts/Padasalai-TAU-Marutham.ttf",
    });

    $('.k-fontName').change(function (e) {
        $("#editor").css({ " font-family": e.currentTarget.value });
    });

    $("#templateTool").kendoDropDownList({
        change: function (e) {
            $("#editor").data("kendoEditor").body.style.backgroundColor = e.sender.value();
        }
    });

    let documentGuid = $('#FileServerId').val();
    let hostName = $('#hdnGroupDocsWebApiURL').val();

    function loadThumbnails() {
        var data = { guid: documentGuid, password: null, IsHTMLMode: 'false' };
        ajaxCallFunctionGroupDocsDocViewer("POST", hostName, "/api/GroupDocsViewerApi/loadThumbnails", JSON.stringify(data), function (returnedData) {
            if (returnedData.message != undefined) {
                return;
            }
            $.each(returnedData.pages, function (index, elem) {
                var pageNumber = elem.Number;
                $('#gd-thumbnails-panzoom').append(
                    '<div id="gd-thumbnails-page-' + pageNumber + '" class="gd-page" style="padding: 10px;"><span> Page: ' + pageNumber + ' </span>' + '</div>'
                );
                renderThumbnails(pageNumber, elem);
            });
        });
    }

    function isPageLoaded(selector) {
        return new Promise(function (resolve, reject) {
            // check if loaded
            var waitForEl = function (selector, count) {
                var count = 0;
                var el = selector.find(".gd-wrapper");
                // check if element is loaded
                if (el.length > 0) {
                    resolve(el);
                } else {
                    // wait 100 milliseconds and check again

                    setTimeout(function () {

                        if (typeof count != "undefined" && count != null) {
                            count++;
                            if (count < 120) {
                                waitForEl(selector, count);
                            } else {
                                reject();
                            }
                        }
                    }, 1000);
                }
            };
            waitForEl(selector);
        });
    }

    function renderThumbnails(pageNumber, pageData) {
        var gd_thumbnails_page = $('#gd-thumbnails-page-' + pageNumber);
        var data = { guid: documentGuid, page: pageNumber, password: null };
        ajaxCallFunctionGroupDocsDocViewer("POST", hostName, "/api/GroupDocsViewerApi/loadDocumentPage", JSON.stringify(data), function (htmlData) {
            gd_thumbnails_page.append('<div class="gd-wrapper">' +
                '<image style="width: 250px;" class="gd-page-image" src="data:image/png;base64,' + htmlData.data + '" alt></image>' +
                '</div>');

            // rotate page if it were rotated earlier
            if (pageData.angle != 0) {
                gd_thumbnails_page.css('animation', 'none');
                gd_thumbnails_page.css('transition-property', 'none');
                gd_thumbnails_page.css('transform', 'rotate(' + pageData.angle + 'deg)');
                if (pageData.angle == 90 || pageData.angle == 270) {
                    if (gd_thumbnails_page.width() > gd_thumbnails_page.height()) {
                        gd_thumbnails_page.addClass("gd-thumbnails-landscape-image-rotated");
                    } else {
                        gd_thumbnails_page.addClass("gd-thumbnails-landscape-image");
                    }
                    gd_thumbnails_page.find("img").removeClass("gd-page-image");
                } else {
                    gd_thumbnails_page.removeClass("gd-thumbnails-landscape");
                    gd_thumbnails_page.removeClass("gd-thumbnails-landscape-image");
                }
            }
        });
    }

    loadThumbnails();

    $('#btnSave').click(function (e) {
        var editor = $("#editor").data("kendoEditor");
        var editorContent = editor.body.innerHTML
        e.preventDefault();
        let SaveDetails = {
            FileId: $('#FileId').val(),
            GUID: $('#FileServerId').val(),
            EditorData: editorContent,
        }

        ajaxCallFunction("POST", "/api/Document/Document/SaveWordFile", JSON.stringify(SaveDetails), function (response) {
            /*window.close();*/
            Utils.showAlert(response.ReturnMessage);
        });
    });
</script>

Now I want to do the same with PPTX file, how can I achieve the same functionality.

@Niteen_Jadhav

To achieve similar functionality with PPTX files using GroupDocs.Editor, you can follow a structure similar to the one you used for DOCX files. Below is a step-by-step guide along with code examples to help you implement this.

Step 1: Create an Empty PPTX File

You can create an empty PPTX file using the Document class from GroupDocs.Editor. Here’s how you can do it:

[HttpPost]
[Route("api/GroupDocsApi/CreateEmptyPptxFile")]
public async Task<SPResponse> CreateEmptyPptxFile(DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest request)
{
    try
    {
        Document document = new Document();
        await Task.Run(() => document.Save(request.FileUploadPath));
        return new SPResponse() { ReturnStatus = "0" };
    }
    catch (Exception ex)
    {
        return new SPResponse() { ReturnStatus = "-1", ReturnMessage = ex.Message };
    }
}

Step 2: Load the PPTX File for Editing

To load a PPTX file, you can use the Editor class along with PresentationEditOptions. Here’s an example:

[HttpPost]
[Route("api/GroupDocsApi/LoadPptxFile")]
public async Task<SPResponse> LoadPptxFile(DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest request)
{
    try
    {
        SPResponse sp = new SPResponse() { ReturnStatus = "-1" };

        using (Editor editor = new Editor(request.FileUploadPath))
        {
            PresentationEditOptions options = new PresentationEditOptions();
            EditableDocument editableDocument = editor.Edit(options);

            string htmlWithStyles = editableDocument.GetEmbeddedHtml();
            sp.Ref1 = htmlWithStyles;
            sp.ReturnStatus = "0";
        }
        return sp;
    }
    catch (Exception ex)
    {
        return new SPResponse() { ReturnStatus = "-1", ReturnMessage = ex.Message };
    }
}

Step 3: Save the Edited PPTX File

To save the edited content back to a PPTX file, you can use the following code:

[HttpPost]
[Route("api/GroupDocsApi/SavePptxFile")]
public SPResponse SavePptxFile(DocPro.DMS.BusinessEntities.Request.GroupDocs.GetDocumentInfoRequest request)
{
    SPResponse sp = new SPResponse() { ReturnStatus = "-1" };
    string editedHtmlContent = request.EditorData;

    string inputFilePath = Path.Combine(request.StoragePath, request.GUID);
    string guid = Guid.NewGuid().ToString();
    string extension = Path.GetExtension(request.GUID);
    string outputFilePath = Path.Combine(request.StoragePath, guid + "." + extension);

    EditableDocument afterEdit = EditableDocument.FromMarkup($"<body>{editedHtmlContent}</body>", new List<IHtmlResource>());
    GroupDocs.Editor.Options.PresentationSaveOptions saveOptions = new GroupDocs.Editor.Options.PresentationSaveOptions();
    try
    {
        using (Editor editor = new Editor(inputFilePath))
        {
            editor.Save(afterEdit, outputFilePath, saveOptions);
        }
        sp.ReturnStatus = "0";
        sp.Ref1 = guid;
    }
    catch (Exception ex)
    {
        throw;
    }
    return sp;
}

Front-End Integration

You can use a similar front-end setup as you did for the DOCX file. Just ensure that the editor you are using supports the HTML content generated from the PPTX file.

Conclusion

By following these steps, you can implement functionality for editing PPTX files in a manner similar to what you have for DOCX files. Make sure to adjust the front-end code to handle the specific requirements of PPTX editing.

If you have any further questions or need additional assistance, feel free to ask!

I don’t think this will work with pptx file as the files are being displayed page wise

@Niteen_Jadhav
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): EDITORNET-2882

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

Hello @Niteen_Jadhav,

Below is how you can achieve PPTX (Presentation) editing functionality similar to DOCX editing using the GroupDocs.Editor library. I’ve included the sample code, references to the commit that demonstrates usage, and a step-by-step explanation. This is based on the example you already have for Word files, adapted for presentations.


1. Reference Commit for Sample Implementation

GroupDocs provided a sample commit showing how to handle Presentation files:

Commit cffb1b010a78f4eb4332e90786c517af553b6ae2

This commit demonstrates how to use the Editor, PresentationEditOptions, and PresentationSaveOptions classes for editing and saving presentations in PPTX format.


2. Controller Code for Editing PPTX Files

Below is a simplified version of the controller logic to load, edit, and save a PPTX file. The key difference from the DOCX workflow is using the PresentationEditOptions for reading slides and PresentationSaveOptions for saving.

Presentation Action

public ActionResult Presentation(int slideIndex)
{
    // Path to your PPTX file (original)
    var filePath = Server.MapPath("~/Content/basic-presentation.pptx");

    // Initialize the Editor instance
    using (Editor editor = new Editor(filePath))
    {
        // Configure options for presentation files
        PresentationEditOptions options = new PresentationEditOptions
        {
            // Specify which slide to edit
            SlideNumber = slideIndex
        };

        // Edit the specified slide
        EditableDocument editableDocument = editor.Edit(options);

        // The HTML with embedded styles for the editor
        var resp = new PresentationHtmlResponse
        {
            FileName = "basic-presentation.pptx",
            Content = editableDocument.GetEmbeddedHtml(),
            SlideIndex = slideIndex
        };

        // Return a strongly typed View, passing the HTML content
        return View(resp);
    }
}
  1. slideIndex is passed in as a parameter so you can open a specific slide in the file.
  2. PresentationEditOptions allows specifying a SlideNumber to edit.
  3. EditableDocument.GetEmbeddedHtml() extracts HTML for that particular slide.

SavePresentation Action

[HttpPost]
public JsonResult SavePresentation(SavePresentationDetailsModel saveDetails)
{
    if (saveDetails == null)
    {
        return Json(new { Success = false, Message = "Invalid data received." });
    }

    try
    {
        // Original file & edited file paths
        var filePathOriginal = Server.MapPath("~/Content/basic-presentation.pptx");
        var filePath = Server.MapPath("~/Content/presentation_edited.pptx");

        // Convert the raw HTML content back into EditableDocument
        EditableDocument afterEdit = EditableDocument.FromMarkup(saveDetails.EditorData, new List<IHtmlResource>());

        // Configure save options for PPTX
        PresentationSaveOptions saveOptions = new PresentationSaveOptions(PresentationFormats.Pptx)
        {
            // SlideNumber is 1-based
            SlideNumber = saveDetails.SlideIndex + 1
        };

        using (Editor editor = new Editor(filePathOriginal))
        {
            // Save the edited content back to PPTX
            editor.Save(afterEdit, filePath, saveOptions);
        }

        // Return success response
        return Json(new { Success = true, Message = $"File saved successfully to {filePath}" });
    }
    catch (Exception ex)
    {
        return Json(new { Success = false, Message = ex.Message });
    }
}
  1. EditableDocument.FromMarkup(...) re-wraps the HTML from the editor back into a format GroupDocs can interpret.
  2. PresentationSaveOptions is used for saving PPTX. Note that SlideNumber = saveDetails.SlideIndex + 1 since slides are 1-based.
  3. You can store the edited file under a new name (presentation_edited.pptx) or overwrite the original (depending on your use case).

3. Razor View for PPTX Editing

Here is the corresponding Razor View (Presentation.cshtml) that:

  1. Displays the current slide.
  2. Allows navigation to previous or next slides (via @Html.ActionLink).
  3. Hosts the Kendo Editor to enable editing of the slide’s HTML content.
  4. Submits the edited content to SavePresentation for saving.
@model GroupDocs.Editor.Kendo.Sample.Controllers.PresentationHtmlResponse

@{
    ViewBag.Title = Model.FileName;
}

<!-- CSS -->
<link href="https://kendo.cdn.telerik.com/2019.2.514/styles/kendo.default-v2.min.css" rel="stylesheet" />

<!-- JavaScript -->
<script src="https://kendo.cdn.telerik.com/2019.2.514/js/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2019.2.514/js/kendo.all.min.js"></script>

<h2>@Html.DisplayFor(a => a.FileName)</h2>

<!-- Keep the current slide index so we know which slide to load/save -->
@Html.HiddenFor(a => a.SlideIndex)

<form id="PresentationEditorForm">
    <div id="dv_Editor">
        <textarea id="editor" rows="4" cols="32" style="width: 990pt; height: 600pt;">
            @Html.Raw(Model.Content)
        </textarea><br />
    </div>

    <!-- Slide Navigation -->
    @if (Model.SlideIndex > 0)
    {
        @Html.ActionLink("Previous Slide", "Presentation", "Home",
            new { slideIndex = Model.SlideIndex - 1 },
            new { @class = "btn" })
    }
    @Html.ActionLink("Next Slide", "Presentation", "Home",
        new { slideIndex = Model.SlideIndex + 1 },
        new { @class = "btn" })
</form>

<script>
    // Initialize Kendo Editor with the required tools
    $("#editor").kendoEditor({
        tools: [
            "bold","italic","underline","strikethrough","justifyLeft","justifyCenter","justifyRight","justifyFull",
            "insertUnorderedList","insertOrderedList","indent","outdent","createLink","unlink","insertImage",
            "subscript","superscript","tableWizard","createTable","addRowAbove","addRowBelow","addColumnLeft","addColumnRight",
            "deleteRow","deleteColumn","mergeCellsHorizontally","mergeCellsVertically","splitCellHorizontally","splitCellVertically",
            "viewHtml","formatting","cleanFormatting","copyFormat","applyFormat","fontSize","foreColor","backColor","pdf",
            {
                name: "fontName",
                items: [
                    { text: "Andale Mono", value: "Andale Mono" },
                    { text: "Arial", value: "Arial" },
                ]
            },
            {
                name: "custom",
                tooltip: "Add page break",
                exec: function (e) {
                    var editor = $(this).data("kendoEditor");
                    editor.exec("inserthtml", { value: "<p style='page-break-before: always'>Page break here!</p>" });
                }
            },
            {
                // Custom Save button for GroupDocs
                name: "customSave",
                tooltip: "GroupDocs.Editor Save",
                template: '<button id="btnSave" class="k-button k-button-icontext"><span class="k-icon k-i-save"></span>Save</button>',
            }
        ],
    });

    // Save PPTX using an AJAX call to SavePresentation
    var editor = $("#editor").data("kendoEditor");
    $('#btnSave').click(function(e){
        e.preventDefault();

        let rawHtmlContent = editor.body.innerHTML; // The editor's HTML
        let slideIndex = $('#SlideIndex').val();

        let saveDetails = {
            EditorData: rawHtmlContent,
            SlideIndex: slideIndex
        };

        $.ajax({
            type: "POST",
            url: "/Home/SavePresentation",
            data: JSON.stringify(saveDetails),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (response) {
                if (response.Success) {
                    alert(response.Message);
                } else {
                    alert("Error: " + response.Message);
                }
            },
            error: function (xhr, status, error) {
                alert("An error occurred: " + error);
            }
        });
    });
</script>

Key Points in the View

  1. slideIndex is stored in a hidden field.
  2. We use the Kendo Editor for the body of the slide.
  3. Navigation links let you move previous / next to handle multi-slide editing.
  4. The Save button is customized via the tools array to call your SavePresentation endpoint.

4. Summary

With the above approach:

  • Loading a Slide
    Use PresentationEditOptions with SlideNumber to extract and display the HTML content for the chosen slide.

  • Editing
    The user edits the HTML in the Kendo Editor (or any WYSIWYG editor).

  • Saving
    On save, wrap the HTML with EditableDocument.FromMarkup() and use PresentationSaveOptions to persist your changes back into a PPTX file.

That’s it! This way, you replicate the same editing cycle you had for DOCX but now tailored for PPTX files. For further reference, you can consult the commit linked above or the official GroupDocs.Editor documentation for more advanced options.

If you have any follow-up questions or need additional clarifications, feel free to let me know!

**Best Regards, **

ERROR: CS0246 The type or namespace name ‘PresentationHtmlResponse’ could not be found (are you missing a using directive or an assembly reference?)

Groupdocs.Editor Version=24.12.0.0

Hello @Niteen_Jadhav,

The error CS0246: The type or namespace name 'PresentationHtmlResponse' could not be found occurs because PresentationHtmlResponse is not part of the official GroupDocs.Editor library. It’s simply a custom class used in the sample code to demonstrate how you might store the necessary data (e.g., FileName, Content, and SlideIndex) for your presentation.

To resolve this, you should:

  1. Create your own response model (e.g., PresentationHtmlResponse or a different name) with the properties you need—like FileName, Content, and SlideIndex.
  2. Include that class in your project’s namespace and reference it where needed.

Hope this helps! If you have any other questions, let us know.