net dotnet convert c# pdf ms-word .net-core

c# - dotnet - net core 2 html to pdf



Convierta los documentos de Word y docx a PDF en.NET Core sin Microsoft.Office.Interop (3)

Usando el binario de LibreOffice

El proyecto LibreOffice es una alternativa multiplataforma de código abierto para MS Office. Podemos usar sus capacidades para exportar archivos doc y docx a PDF . Actualmente, LibreOffice no tiene una API oficial para .NET, por lo tanto, hablaremos directamente con el binario de soffice .

Es un tipo de solución "pirata", pero creo que es la solución con menos errores y costos posibles. Otra ventaja de este método es que no está restringido a la conversión de doc y docx : puede convertirlo desde cualquier formato de soporte de LibreOffice (por ejemplo, odt, html, hoja de cálculo y más).

La implementación

Escribí un programa c# simple que usa el binario soffice . Esto es solo una prueba de concepto (y mi primer programa en c# ). Admite Windows out of the box y Linux solo si se ha instalado el paquete de LibreOffice.

Esto es main.cs :

using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Reflection; namespace DocToPdf { public class LibreOfficeFailedException : Exception { public LibreOfficeFailedException(int exitCode) : base(string.Format("LibreOffice has failed with {}", exitCode)) {} } class Program { static string getLibreOfficePath() { switch (Environment.OSVersion.Platform) { case PlatformID.Unix: return "/usr/bin/soffice"; case PlatformID.Win32NT: string binaryDirectory = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); return binaryDirectory + "//Windows//program//soffice.exe"; default: throw new PlatformNotSupportedException ("Your OS is not supported"); } } static void Main(string[] args) { string libreOfficePath = getLibreOfficePath(); // FIXME: file name escaping: I have not idea how to do it in .NET. ProcessStartInfo procStartInfo = new ProcessStartInfo(libreOfficePath, string.Format("--convert-to pdf --nologo {0}", args[0])); procStartInfo.RedirectStandardOutput = true; procStartInfo.UseShellExecute = false; procStartInfo.CreateNoWindow = true; procStartInfo.WorkingDirectory = Environment.CurrentDirectory; Process process = new Process() { StartInfo = procStartInfo, }; process.Start(); process.WaitForExit(); // Check for failed exit code. if (process.ExitCode != 0) { throw new LibreOfficeFailedException(process.ExitCode); } } } }

Recursos

Resultados

Lo había probado en Arch Linux, compilado con mono . Lo ejecuto usando mon y el binario de Linux, y con wine : usando el binario de Windows.

Puedes encontrar los resultados en el directorio de Tests :

Archivos de entrada: testdoc.doc , testdocx.docx

Salidas:

Necesito mostrar los archivos de Word .doc y .docx en un navegador. No hay una forma real del lado del cliente para hacer esto y estos documentos no se pueden compartir con Google Docs o Microsoft Office 365 por razones legales.

Los navegadores no pueden mostrar Word, pero pueden mostrar PDF, así que quiero convertir estos documentos a PDF en el servidor y luego mostrarlos.

Sé que esto se puede hacer usando Microsoft.Office.Interop.Word , pero mi aplicación es .NET Core y no tiene acceso a la interoperabilidad de Office. Podría estar ejecutándose en Azure, pero también podría estar ejecutándose en un contenedor Docker en cualquier otra cosa.

Parece que hay muchas preguntas similares a esto, sin embargo, la mayoría está preguntando sobre full-framework .NET o suponiendo que el servidor es un sistema operativo Windows y cualquier respuesta no me sirve.

¿Cómo convierto archivos .doc y .docx a .pdf sin acceso a Microsoft.Office.Interop.Word ?


Este fue un PITA, no es de extrañar que todas las soluciones de terceros cobren $ 500 por desarrollador.

Una buena noticia es que el Open XML SDK recientemente agregó soporte para .Net Standard, por lo que parece que estás de suerte con el formato .docx .

Malas noticias por el momento, no hay muchas opciones para las bibliotecas de generación de PDF en .NET Core. Dado que no parece que usted quiera pagar por uno y no puede utilizar legalmente un servicio de terceros, no tenemos otra opción, excepto el nuestro.

El principal problema es conseguir que el contenido del documento de Word se transforme a PDF. Una de las formas populares es leer el Docx en HTML y exportarlo a PDF. Fue difícil de encontrar, pero hay una versión .Net Core de OpenXMLSDK- PowerTools que admite la transformación de Docx a HTML. La solicitud de extracción está "a punto de ser aceptada", puede obtenerla desde aquí:

https://github.com/OfficeDev/Open-Xml-PowerTools/tree/abfbaac510d0d60e2f492503c60ef897247716cf

Ahora que podemos extraer el contenido del documento a HTML, necesitamos convertirlo a PDF. Existen algunas bibliotecas para convertir HTML a PDF, por ejemplo, DinkToPdf es un contenedor multiplataforma alrededor del HTML de Webkit a la biblioteca de PDF libwkhtmltox.

Pensé que DinkToPdf era mejor que https://code.msdn.microsoft.com/How-to-export-HTML-to-PDF-c5afd0ce

Docx a HTML

Pongamos todo esto, descargue el proyecto OpenXMLSDK-PowerTools .Net Core y genérelo (solo OpenXMLPowerTools.Core y OpenXMLPowerTools.Core.Example - ignore el otro proyecto). Configure el proyecto OpenXMLPowerTools.Core.Example como inicio. Ejecuta el proyecto de consola:

static void Main(string[] args) { var source = Package.Open(@"test.docx"); var document = WordprocessingDocument.Open(source); HtmlConverterSettings settings = new HtmlConverterSettings(); XElement html = HtmlConverter.ConvertToHtml(document, settings); Console.WriteLine(html.ToString()); var writer = File.CreateText("test.html"); writer.WriteLine(html.ToString()); writer.Dispose(); Console.ReadLine();

Si ejecuta el proyecto, verá que el HTML se ve casi exactamente como el contenido del documento de Word:

Sin embargo, si prueba un documento de Word con imágenes o enlaces, notará que faltan o están rotos.

Este artículo de CodeProject trata estos temas: https://www.codeproject.com/Articles/1162184/Csharp-Docx-to-HTML-to-Docx

Tuve que cambiar el método static Uri FixUri(string brokenUri) para devolver un Uri y agregué mensajes de error fáciles de usar.

static void Main(string[] args) { var fileInfo = new FileInfo(@"c:/temp/MyDocWithImages.docx"); string fullFilePath = fileInfo.FullName; string htmlText = string.Empty; try { htmlText = ParseDOCX(fileInfo); } catch (OpenXmlPackageException e) { if (e.ToString().Contains("Invalid Hyperlink")) { using (FileStream fs = new FileStream(fullFilePath,FileMode.OpenOrCreate, FileAccess.ReadWrite)) { UriFixer.FixInvalidUri(fs, brokenUri => FixUri(brokenUri)); } htmlText = ParseDOCX(fileInfo); } } var writer = File.CreateText("test1.html"); writer.WriteLine(htmlText.ToString()); writer.Dispose(); } public static Uri FixUri(string brokenUri) { string newURI = string.Empty; if (brokenUri.Contains("mailto:")) { int mailToCount = "mailto:".Length; brokenUri = brokenUri.Remove(0, mailToCount); newURI = brokenUri; } else { newURI = " "; } return new Uri(newURI); } public static string ParseDOCX(FileInfo fileInfo) { try { byte[] byteArray = File.ReadAllBytes(fileInfo.FullName); using (MemoryStream memoryStream = new MemoryStream()) { memoryStream.Write(byteArray, 0, byteArray.Length); using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) { int imageCounter = 0; var pageTitle = fileInfo.FullName; var part = wDoc.CoreFilePropertiesPart; if (part != null) pageTitle = (string)part.GetXDocument() .Descendants(DC.title) .FirstOrDefault() ?? fileInfo.FullName; WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings() { AdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }", PageTitle = pageTitle, FabricateCssClasses = true, CssClassPrefix = "pt-", RestrictToSupportedLanguages = false, RestrictToSupportedNumberingFormats = false, ImageHandler = imageInfo => { ++imageCounter; string extension = imageInfo.ContentType.Split(''/'')[1].ToLower(); ImageFormat imageFormat = null; if (extension == "png") imageFormat = ImageFormat.Png; else if (extension == "gif") imageFormat = ImageFormat.Gif; else if (extension == "bmp") imageFormat = ImageFormat.Bmp; else if (extension == "jpeg") imageFormat = ImageFormat.Jpeg; else if (extension == "tiff") { extension = "gif"; imageFormat = ImageFormat.Gif; } else if (extension == "x-wmf") { extension = "wmf"; imageFormat = ImageFormat.Wmf; } if (imageFormat == null) return null; string base64 = null; try { using (MemoryStream ms = new MemoryStream()) { imageInfo.Bitmap.Save(ms, imageFormat); var ba = ms.ToArray(); base64 = System.Convert.ToBase64String(ba); } } catch (System.Runtime.InteropServices.ExternalException) { return null; } ImageFormat format = imageInfo.Bitmap.RawFormat; ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders() .First(c => c.FormatID == format.Guid); string mimeType = codec.MimeType; string imageSource = string.Format("data:{0};base64,{1}", mimeType, base64); XElement img = new XElement(Xhtml.img, new XAttribute(NoNamespace.src, imageSource), imageInfo.ImgStyleAttribute, imageInfo.AltText != null ? new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); return img; } }; XElement htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); var html = new XDocument(new XDocumentType("html", null, null, null), htmlElement); var htmlString = html.ToString(SaveOptions.DisableFormatting); return htmlString; } } } catch { return "The file is either open, please close it or contains corrupt data"; } }

Ahora podemos obtener imágenes:

Si solo desea mostrar los archivos .docx de Word en un navegador web, es mejor que no convierta el HTML a PDF, ya que eso aumentará significativamente el ancho de banda. Puede almacenar el HTML en un sistema de archivos, en la nube o en un dB utilizando una tecnología VPP.

HTML a PDF

Lo siguiente que debemos hacer es pasar el HTML a DinkToPdf. Descargue la solución DinkToPdf (90 MB). Cree la solución: tomará un tiempo hasta que todos los paquetes se restauren y la solución se compile.

IMPORTANTE:

La biblioteca DinkToPdf requiere el archivo libwkhtmltox.so y libwkhtmltox.dll en la raíz de su proyecto si desea ejecutar en Linux y Windows. También hay un archivo libwkhtmltox.dylib para Mac si lo necesita.

Estas dlls están en la carpeta v0.12.4. Dependiendo de su PC, 32 o 64 bits, copie los 3 archivos a la carpeta DinkToPdf-master / DinkToPfd.TestConsoleApp / bin / Debug / netcoreapp1.1.

IMPORTANTE 2:

Asegúrese de tener libgdiplus instalado en su imagen de Docker o en su máquina Linux. La biblioteca libwkhtmltox.so depende de ello.

Establezca DinkToPfd.TestConsoleApp como proyecto de Inicio y cambie el archivo Program.cs para leer el contenido HTML del archivo HTML guardado con Open-Xml-PowerTools en lugar del texto Ipsom de Lorium.

var doc = new HtmlToPdfDocument() { GlobalSettings = { ColorMode = ColorMode.Color, Orientation = Orientation.Landscape, PaperSize = PaperKind.A4, }, Objects = { new ObjectSettings() { PagesCount = true, HtmlContent = File.ReadAllText(@"C:/TFS/Sandbox/Open-Xml-PowerTools-abfbaac510d0d60e2f492503c60ef897247716cf/ToolsTest/test1.html"), WebSettings = { DefaultEncoding = "utf-8" }, HeaderSettings = { FontSize = 9, Right = "Page [page] of [toPage]", Line = true }, FooterSettings = { FontSize = 9, Right = "Page [page] of [toPage]" } } } };

El resultado de Docx vs PDF es bastante impresionante y dudo que muchas personas puedan distinguir muchas diferencias (especialmente si nunca ven el original):

PD. Me doy cuenta de que querías convertir .doc y .docx a PDF. Yo sugeriría hacer un servicio para convertir .doc a docx usando una tecnología específica de Windows / Microsoft que no sea de servidor. El formato de documento es binario y no está destinado a la automatización de la oficina del lado del servidor .


Recientemente he hecho esto con spire.doc. Tiene un límite de 3 páginas para la versión gratuita, pero puede convertir fácilmente un archivo docx a PDF usando algo como esto

private void ConvertToPdf() { try { for (int i = 0; i < listOfDocx.Count; i++) { CurrentModalText = "Converting To PDF"; CurrentLoadingNum += 1; string savePath = PdfTempStorage + i + ".pdf"; listOfPDF.Add(savePath); Spire.Doc.Document document = new Spire.Doc.Document(listOfDocx[i], FileFormat.Auto); document.SaveToFile(savePath, FileFormat.PDF); } } catch (Exception e) { throw e; } }

Luego coso estos pdfs individuales más tarde usando itextsharp.pdf

public static byte[] concatAndAddContent(List<byte[]> pdfByteContent, List<MailComm> localList) { using (var ms = new MemoryStream()) { using (var doc = new Document()) { using (var copy = new PdfSmartCopy(doc, ms)) { doc.Open(); //add checklist at the start using (var db = new StudyContext()) { var contentId = localList[0].ContentID; var temp = db.MailContentTypes.Where(x => x.ContentId == contentId).ToList(); if (!temp[0].Code.Equals("LAB")) { pdfByteContent.Insert(0, CheckListCreation.createCheckBox(localList)); } } //Loop through each byte array foreach (var p in pdfByteContent) { //Create a PdfReader bound to that byte array using (var reader = new PdfReader(p)) { //Add the entire document instead of page-by-page copy.AddDocument(reader); } } doc.Close(); } } //Return just before disposing return ms.ToArray(); } }

No sé si esto se adapta a su caso de uso, ya que no ha especificado el tamaño de los documentos que está intentando escribir, pero si son> 3 páginas o puede manipularlos para que tengan menos de 3 páginas, le permitirá convertirlos. en pdfs