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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
ENV SYNCFUSION_LICENSE_KEY=""
ENV SPELLCHECK_DICTIONARY_PATH=""
ENV SPELLCHECK_JSON_FILENAME=""
ENV SPELLCHECK_CACHE_COUNT=""
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build

WORKDIR /source
COPY ["src/ej2-documenteditor-server/ej2-documenteditor-server.csproj", "./ej2-documenteditor-server/ej2-documenteditor-server.csproj"]
Expand All @@ -19,6 +19,7 @@ FROM build AS publish
RUN dotnet publish -c Release -o /app

FROM base AS final
RUN apt-get update && apt-get install -y libfontconfig
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "ej2-documenteditor-server.dll"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The [Syncfusion **Word Processor (also known as Document Editor)**](https://www.syncfusion.com/javascript-ui-controls/js-word-processor?utm_source=docker&utm_medium=listing&utm_campaign=javascript-word-processor-docker) is a component with editing capabilities like Microsoft Word. It is used to create, edit, view, and print Word documents. It provides all the common word processing abilities, including editing text; formatting contents; resizing images and tables; finding and replacing text; importing, exporting, and printing Word documents; and using bookmarks and tables of contents.

This Docker project is the predefined Docker container for Syncfusion’s Word Processor backend functionalities. This server-side Web API project is targeting ASP.NET Core 6.0.
This Docker project is the predefined Docker container for Syncfusion’s Word Processor backend functionalities. This server-side Web API project is targeting ASP.NET Core 8.0.

If you want to add new functionality or customize any existing functionalities, then create your own Docker file by referencing this Docker project.

Expand Down
280 changes: 276 additions & 4 deletions src/ej2-documenteditor-server/Controllers/DocumentEditorController.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Text;
using System.IO;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Syncfusion.EJ2.DocumentEditor;
using Syncfusion.DocIORenderer;
using Syncfusion.Pdf;
using WDocument = Syncfusion.DocIO.DLS.WordDocument;
using WFormatType = Syncfusion.DocIO.FormatType;
using Syncfusion.EJ2.SpellChecker;
using SkiaSharp;
using BitMiracle.LibTiff.Classic;

namespace EJ2DocumentEditorServer.Controllers
{
Expand All @@ -22,7 +28,7 @@ public class DocumentEditorController : Controller
public DocumentEditorController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
path = Startup.path;
path = Program.path;
}

[AcceptVerbs("Post")]
Expand All @@ -46,7 +52,6 @@ public string Import(IFormCollection data)
WordDocument document = WordDocument.Load(stream, GetFormatType(type.ToLower()));
//Unhooks MetafileImageParsed event.
WordDocument.MetafileImageParsed -= OnMetafileImageParsed;

string json = Newtonsoft.Json.JsonConvert.SerializeObject(document);
document.Dispose();
return json;
Expand All @@ -55,10 +60,82 @@ public string Import(IFormCollection data)
//Converts Metafile to raster image.
private static void OnMetafileImageParsed(object sender, MetafileImageParsedEventArgs args)
{
//You can write your own method definition for converting metafile to raster image using any third-party image converter.
args.ImageStream = ConvertMetafileToRasterImage(args.MetafileStream);
if (args.IsMetafile)
{
//MetaFile image conversion(EMF and WMF)
//You can write your own method definition for converting metafile to raster image using any third-party image converter.
args.ImageStream = ConvertMetafileToRasterImage(args.MetafileStream);
}
else
{
//TIFF image conversion
args.ImageStream = TiffToPNG(args.MetafileStream);

}
}

// Converting Tiff to Png image using Bitmiracle https://www.nuget.org/packages/BitMiracle.LibTiff.NET
private static MemoryStream TiffToPNG(Stream tiffStream)
{
MemoryStream imageStream = new MemoryStream();
using (Tiff tif = Tiff.ClientOpen("in-memory", "r", tiffStream, new TiffStream()))
{
// Find the width and height of the image
FieldValue[] value = tif.GetField(BitMiracle.LibTiff.Classic.TiffTag.IMAGEWIDTH);
int width = value[0].ToInt();

value = tif.GetField(BitMiracle.LibTiff.Classic.TiffTag.IMAGELENGTH);
int height = value[0].ToInt();

// Read the image into the memory buffer
int[] raster = new int[height * width];
if (!tif.ReadRGBAImage(width, height, raster))
{
throw new Exception("Could not read image");
}

// Create a bitmap image using SkiaSharp.
using (SKBitmap sKBitmap = new SKBitmap(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Premul))
{
// Convert a RGBA value to byte array.
byte[] bitmapData = new byte[sKBitmap.RowBytes * sKBitmap.Height];
for (int y = 0; y < sKBitmap.Height; y++)
{
int rasterOffset = y * sKBitmap.Width;
int bitsOffset = (sKBitmap.Height - y - 1) * sKBitmap.RowBytes;

for (int x = 0; x < sKBitmap.Width; x++)
{
int rgba = raster[rasterOffset++];
bitmapData[bitsOffset++] = (byte)((rgba >> 16) & 0xff);
bitmapData[bitsOffset++] = (byte)((rgba >> 8) & 0xff);
bitmapData[bitsOffset++] = (byte)(rgba & 0xff);
bitmapData[bitsOffset++] = (byte)((rgba >> 24) & 0xff);
}
}

// Convert a byte array to SKColor array.
SKColor[] sKColor = new SKColor[bitmapData.Length / 4];
int index = 0;
for (int i = 0; i < bitmapData.Length; i++)
{
sKColor[index] = new SKColor(bitmapData[i + 2], bitmapData[i + 1], bitmapData[i], bitmapData[i + 3]);
i += 3;
index += 1;
}

// Set the SKColor array to SKBitmap.
sKBitmap.Pixels = sKColor;

// Save the SKBitmap to PNG image stream.
sKBitmap.Encode(SKEncodedImageFormat.Png, 100).SaveTo(imageStream);
imageStream.Flush();
}
}
return imageStream;
}


private static Stream ConvertMetafileToRasterImage(Stream ImageStream)
{
//Here we are loading a default raster image as fallback.
Expand Down Expand Up @@ -296,6 +373,10 @@ internal static WFormatType GetWFormatType(string format)
return WFormatType.WordML;
case ".odt":
return WFormatType.Odt;
case ".html":
return WFormatType.Html;
case ".md":
return WFormatType.Markdown;
default:
throw new NotSupportedException("EJ2 DocumentEditor does not support this file format.");
}
Expand Down Expand Up @@ -336,15 +417,116 @@ public FileStreamResult Export(IFormCollection data)
case WFormatType.WordML:
contentType = "application/xml";
break;
case WFormatType.Html:
contentType = "application/html";
break;
case WFormatType.Markdown:
contentType = "application/markdown";
break;
case WFormatType.Dotx:
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.template";
break;
case WFormatType.Docx:
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
break;
case WFormatType.Doc:
contentType = "application/msword";
break;
case WFormatType.Dot:
contentType = "application/msword";
break;
}
document.Save(stream, type);
}
document.Close();
stream.Position = 0;
return new FileStreamResult(stream, contentType)
{
FileDownloadName = fileName
};
}
private string RetrieveFileType(string name)
{
int index = name.LastIndexOf('.');
string format = index > -1 && index < name.Length - 1 ?
name.Substring(index) : ".doc";
return format;
}
public class SaveParameter
{
public string Content { get; set; }
public string FileName { get; set; }
public string Format { get; set; }
}
[AcceptVerbs("Post")]
[HttpPost]
[EnableCors("AllowAllOrigins")]
[Route("ServerSideExport")]
public FileStreamResult ServerSideExport([FromBody] SaveParameter data)
{
string fileName = data.FileName;
string format = RetrieveFileType(string.IsNullOrEmpty(data.Format) ? fileName : data.Format);
if (string.IsNullOrEmpty(fileName))
{
fileName = "Document1.docx";
}
WDocument document;
if (format.ToLower() == ".pdf")
{
Stream stream = WordDocument.Save(data.Content, FormatType.Docx);
document = new Syncfusion.DocIO.DLS.WordDocument(stream, Syncfusion.DocIO.FormatType.Docx);
}
else
{
document = WordDocument.Save(data.Content);
}
return SaveDocument(document, format, fileName);
}
private FileStreamResult SaveDocument(WDocument document, string format, string fileName)
{
Stream stream = new MemoryStream();
string contentType = "";
if (format.ToLower() == ".pdf")
{
contentType = "application/pdf";
DocIORenderer render = new DocIORenderer();
PdfDocument pdfDocument = render.ConvertToPDF(document);
stream = new MemoryStream();
pdfDocument.Save(stream);
pdfDocument.Close();
}
else
{
WFormatType type = GetWFormatType(format);
switch (type)
{
case WFormatType.Rtf:
contentType = "application/rtf";
break;
case WFormatType.WordML:
contentType = "application/xml";
break;
case WFormatType.Html:
contentType = "application/html";
break;
case WFormatType.Dotx:
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.template";
break;
case WFormatType.Docx:
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
break;
case WFormatType.Doc:
contentType = "application/msword";
break;
case WFormatType.Dot:
contentType = "application/msword";
break;
case WFormatType.Odt:
contentType = "application/vnd.oasis.opendocument.text";
break;
case WFormatType.Markdown:
contentType = "text/markdown";
break;
}
document.Save(stream, type);
}
Expand Down Expand Up @@ -378,6 +560,96 @@ private WDocument GetDocument(IFormCollection data)
stream.Dispose();
return document;
}

[AcceptVerbs("Post")]
[HttpPost]
[EnableCors("AllowAllOrigins")]
[Route("CompareDocuments")]
public string CompareDocuments([FromForm] CompareParameter data)
{
if (data.OriginalFile == null || data.RevisedFile == null)
return null;
Stream originalDocumentStream = new MemoryStream();
data.OriginalFile.CopyTo(originalDocumentStream);
originalDocumentStream.Position = 0;
Stream revisedDocumentStream = new MemoryStream();
data.RevisedFile.CopyTo(revisedDocumentStream);
revisedDocumentStream.Position = 0;
string json = "";

using (WDocument originalDocument = new WDocument(originalDocumentStream, WFormatType.Docx))
{
//Load the revised document.
using (WDocument revisedDocument = new WDocument(revisedDocumentStream, WFormatType.Docx))
{
// Compare the original and revised Word documents.
originalDocument.Compare(revisedDocument);
WordDocument document = WordDocument.Load(originalDocument);
json = Newtonsoft.Json.JsonConvert.SerializeObject(document);
originalDocument.Dispose();
revisedDocument.Dispose();
document.Dispose();
}
}
return json;
}

[AcceptVerbs("Post")]
[HttpPost]
[EnableCors("AllowAllOrigins")]
[Route("CompareDocumentsFromUrl")]
public string CompareDocumentsFromUrl([FromBody] CompareUrlParameter data)
{
if (data.OriginalFilePath == null || data.RevisedFilePath == null)
return null;
string json = "";
using (WDocument originalDocument = new WDocument(GetDocFromURL(data.OriginalFilePath).Result, WFormatType.Docx))
{
using (WDocument revisedDocument = new WDocument(GetDocFromURL(data.RevisedFilePath).Result, WFormatType.Docx))
{
originalDocument.Compare(revisedDocument);
WordDocument document = WordDocument.Load(originalDocument);
json = Newtonsoft.Json.JsonConvert.SerializeObject(document);
originalDocument.Dispose();
revisedDocument.Dispose();
document.Dispose();
}
}
return json;
}
public class CompareUrlParameter
{
public string OriginalFilePath { get; set; }
public string RevisedFilePath { get; set; }
}
public class CompareParameter
{
public IFormFile OriginalFile { get; set; }
public IFormFile RevisedFile { get; set; }
}

async Task<Stream> GetDocFromURL(string url)
{
string documentPath = Path.Combine(path, url);
Stream stream = null;
if (System.IO.File.Exists(documentPath))
{
byte[] bytes = System.IO.File.ReadAllBytes(documentPath);
stream = new MemoryStream(bytes);
}
else
{
bool result = Uri.TryCreate(url, UriKind.Absolute, out Uri uriResult)
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
if (result)
{
stream = GetDocumentFromURL(url).Result;
if (stream != null)
stream.Position = 0;
}
}
return stream;
}
}

}
Loading