Saltar al contenido

Convierta el formato Word doc y docx a PDF en .NET Core sin Microsoft.Office.Interop

Basta ya de buscar por otros sitios ya que estás al lugar correcto, contamos con la respuesta que deseas sin complicarte.

Solución:

Esto fue una molestia, no es de extrañar que todas las soluciones de terceros estén cobrando $ 500 por desarrollador.

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

Malas noticias en este momento no hay muchas opciones para las bibliotecas de generación de PDF en .NET Core. Dado que no parece que quieras pagar por uno y no puedes usar legalmente un servicio de terceros, no tenemos otra opción que no sea el nuestro.

El principal problema es transformar el contenido del documento de Word a PDF. Una de las formas más 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-Herramientas eléctricas 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, debemos convertirlo a PDF. Hay algunas bibliotecas para convertir HTML a PDF, por ejemplo, DinkToPdf es un contenedor multiplataforma alrededor de la biblioteca de HTML a PDF de Webkit libwkhtmltox.

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


Docx a HTML

Pongamos esto en conjunto, descargue el proyecto OpenXMLSDK-PowerTools .Net Core y compárelo (solo OpenXMLPowerTools.Core y OpenXMLPowerTools.Core.Example – ignore el otro proyecto). Establezca OpenXMLPowerTools.Core.Example como proyecto de inicio. Ejecute el proyecto de la 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();

Asegúrese de que test.docx sea un documento de Word válido con algo de texto, de lo contrario, podría obtener un error:

el paquete especificado no es válido. falta la parte principal

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

ingrese la descripción de la imagen aquí

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 aborda estos problemas: https://www.codeproject.com/Articles/1162184/Csharp-Docx-to-HTML-to-Docx

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

static void Main(string[] args)

    var fileInfo = new FileInfo(@"c:tempMyDocWithImages.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";
    

Es posible que necesite el paquete System.Drawing.Common NuGet para usar ImageFormat

Ahora podemos obtener imágenes:

ingrese la descripción de la imagen aquí

Si solo desea mostrar archivos .docx de Word en un navegador web, es mejor no convertir 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: llevará un tiempo restaurar todos los paquetes y compilar la solución.

IMPORTANTE:

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

Estas DLL se encuentran 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 ella.

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

var doc = new HtmlToPdfDocument()

    GlobalSettings = 
        ColorMode = ColorMode.Color,
        Orientation = Orientation.Landscape,
        PaperSize = PaperKind.A4,
    ,
    Objects = 
        new ObjectSettings() 
            PagesCount = true,
            HtmlContent = File.ReadAllText(@"C:TFSSandboxOpen-Xml-PowerTools-abfbaac510d0d60e2f492503c60ef897247716cfToolsTesttest1.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 mucha gente detecte muchas diferencias (especialmente si nunca ven el original):

ingrese la descripción de la imagen aquí

PD. Me doy cuenta de que querías convertir a los dos .doc y .docx a PDF. Sugeriría hacer un servicio usted mismo para convertir .doc a docx utilizando una tecnología específica de Windows / Microsoft que no sea servidor. El formato doc es binario y no está diseñado para la automatización de oficina del lado del servidor.

Usando el binario de LibreOffice

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

Es una especie de solución “pirata”, pero creo que es la solución con menos cantidad de errores y costos de mantenimiento posibles. Otra ventaja de este método es que no está restringido a convertir de doc y docx: puede convertirlo desde cualquier formato compatible con LibreOffice (por ejemplo, odt, html, hoja de cálculo y más).

La implementación

Escribí un simple c# programa que utiliza el soffice binario. Esto es solo una prueba de concepto (y mi primer programa en c#). Es compatible Windows fuera de la caja y Linux solo si se ha instalado el paquete LibreOffice.

Este 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

  • El repositorio del proyecto: ejemplo de un paquete que incluye el binario de Windows LibreOffice.

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.

Puede encontrar los resultados en el directorio Tests:

Archivos de entrada: testdoc.doc, testdocx.docx

Salidas:

  • Vino: testdoc, testdocx.

  • Mono: testdoc, testdocx.

Recientemente hice esto con FreeSpire.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 PDF individuales juntos más tarde usando iTextSharp.pdf:

public static byte[] concatAndAddContent(List pdfByteContent, List 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á tratando de escribir, pero si son> 3 páginas o puede manipularlos para que tengan menos de 3 páginas, le permitirá convertirlos en archivos PDF.

Como se menciona en los comentarios a continuación, tampoco puede ayudar con los idiomas RTL, gracias @Aria por señalarlo.

Sección de Reseñas y Valoraciones

Finalizando este artículo puedes encontrar las críticas de otros administradores, tú asimismo tienes la habilidad mostrar el tuyo si lo crees conveniente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *