Convertir le format Word doc et docx en PDF in.NET Core Sans Microsoft.Bureau.Interop

j'ai besoin d'afficher le Mot .doc et .docx fichiers dans un navigateur. Il n'y a pas de véritable moyen côté client pour le faire et ces documents ne peuvent pas être partagés avec Google docs ou Microsoft Office 365 pour des raisons juridiques.

les navigateurs ne peuvent pas afficher Word, mais peuvent afficher PDF, donc je veux convertir ces docs en PDF sur le serveur et ensuite afficher cela.

je sais que cela peut être fait en utilisant Microsoft.Office.Interop.Word, mais mon application est .NET Core et n'a pas accès à Office interop. Il pourrait il fonctionne sur L'Azure, mais il pourrait aussi fonctionner dans un conteneur Docker sur n'importe quoi d'autre.

il semble qu'il y ait beaucoup de questions similaires à cela, cependant la plupart se posent sur full - framework .NET ou en supposant que le serveur est un OS Windows et que toute réponse ne me soit d'aucune utilité.

comment convertir .doc et .docx fichiers .pdfsans accès Microsoft.Office.Interop.Word?

22
demandé sur Jeremy Thompson 2017-10-05 11:03:47

3 réponses

C'était un tel PITA, pas étonnant que toutes les solutions tierces demandent 500 $par développeur.

les bonnes nouvelles sont les Open XML SDK a récemment ajouté la prise en charge de la norme .Net il semble que vous avez de la chance avec le .docx format.

mauvaises nouvelles pour le moment il n'y a pas beaucoup de choix pour les bibliothèques de génération PDF sur .NET Core. Car il ne regarde pas comme vous voulez payer pour un et vous ne pouvez pas utiliser légalement une 3ème partie, nous avons peu de choix, sauf à rouler notre propre.

le principal problème est de transformer le contenu du document Word en PDF. Une des façons populaires est de lire le Docx en HTML et de l'exporter en PDF. C'était difficile à trouver, mais il y a la version .net Core de L'OpenXMLSDK- PowerTools qui supporte la transformation de Docx en HTML. La demande de Pull est" sur le point d'être acceptée", vous pouvez l'obtenir à partir de ici:

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

maintenant que nous pouvons extraire le contenu D'un document en HTML, nous devons le convertir en PDF. Il existe quelques bibliothèques pour convertir HTML en PDF, par exemple DinkToPdf est un wrapper multi-plateforme autour de la bibliothèque HTML to PDF libwkhtmltox de Webkit.

je pensais que DinkToPdf était mieux que https://code.msdn.microsoft.com/How-to-export-HTML-to-PDF-c5afd0ce


Docx to HTML

mettons tout cela en évidence, téléchargez le projet Openxmlsdk-PowerTools. net Core et construisez-le (juste les OpenXMLPowerTools.Core et les outils Openxmlpower.Core.Exemple - ignorer l'autre projet). Définissez les outils Openxmlpower.Core.Exemple comme projet de Démarrage. Exécutez le projet de console:

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 vous dirigez le projet vous verrez que le HTML ressemble presque exactement au contenu du document Word:

enter image description here

cependant, si vous essayez un document Word avec des images ou des liens, vous remarquerez qu'ils sont manquants ou cassés.

C'CodeProject article répond à ces questions: https://www.codeproject.com/Articles/1162184/Csharp-Docx-to-HTML-to-Docx

j'ai dû changer le static Uri FixUri(string brokenUri) méthode retourne un Uri et j'ai ajouté messages d'erreur conviviaux.

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";
    }
}

Maintenant, nous pouvons obtenir des images:

enter image description here

Si vous souhaitez uniquement afficher Word .les fichiers docx dans un navigateur web il est préférable de ne pas convertir le HTML en PDF car cela augmentera considérablement la bande passante. Vous pouvez stocker le HTML dans un système de fichiers, un cloud, ou dans une base de données en utilisant une technologie VPP.


HTML to PDF

la Prochaine chose que nous devez faire est de passer le code HTML de DinkToPdf. Télécharger la solution DinkToPdf (90 Mo). Construire la solution - cela prendra un certain temps pour que tous les paquets soient restaurés et pour que la solution soit compilée.

IMPORTANT:

la bibliothèque DinkToPdf nécessite le libwkhtmltox.so et libwkhtmltox.dll fichier dans la racine de votre projet si vous souhaitez exécuter sur Linux et Windows. Il y a aussi un libwkhtmltox.fichier dylib pour Mac si vous avez besoin il.

ces dlls sont dans le v0.12.4 folder. En fonction de votre PC, 32 ou 64 bits, copiez les 3 fichiers vers le fichier DinkToPdf-master\DinkToPfd.TestConsoleApp\bin\Debug\netcoreapp1.1 dossier.

IMPORTANT 2:

assurez-vous que libgdiplus est installé dans votre image Docker ou sur votre machine Linux. Le libwkhtmltox.la bibliothèque en dépend.

mettre le DinkToPfd.TestConsoleApp en tant que projet de démarrage et changez le programme.fichier cs pour lire le contenu htmlc du fichier HTML sauvegardé avec Open-Xml-PowerTools au lieu du texte 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]" }
        }
    }
};

le résultat de La Docx vs le PDF est assez impressionnant et je doute que beaucoup de gens choisir de nombreuses différences (surtout si ils ne voient jamais l'original):

enter image description here

Ps. J'ai réalisé que vous vouliez convertir à la fois .doc et .docx to PDF. Je vous suggère de faire un service vous-même. convertir. doc à docx en utilisant une technologie spécifique non-server Windows/Microsoft. Le format doc est binaire et n'est pas destiné à côté serveur d'automatisation de bureau.

29
répondu Jeremy Thompson 2017-10-10 06:42:53

utilisant le binaire LibreOffice

le projet LibreOffice est une alternative libre pour MS Office. Nous pouvons utiliser ses capacités pour exporter doc et docx fichiers PDF. À L'heure actuelle, LibreOffice n'a pas d'API officielle pour .NET, par conséquent, nous parlerons directement au soffice binaire.

c'est une sorte de solution "hacky", mais je pense que c'est la solution avec moins de bugs et des coûts de maintien possibles. Un autre avantage de l' cette méthode est que vous n'êtes pas limité à convertir de doc et docx: vous pouvez le convertir à partir de tous les formats pris en charge par LibreOffice (par exemple odt, html, tableur, et plus).

La mise en œuvre

j'ai écrit un simple c# programme qui utilise le soffice binaire. C'est juste une validation de principe (et mon premier programme en c#). Il prend en charge Windows et Linux seulement si le paquet LibreOffice a été installer.

C'est 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);
            }
        }
    }
}

Ressources

Résultats

Je l'avais testé sur Arch Linux, compilé avec mono. Je l'exécute en utilisant mon et le binaire Linux, et avec wine: utiliser le binaire Windows.

Vous pouvez trouver les résultats dans le Tests répertoire:

fichiers d'Entrée: testdoc.doc, testdocx.docx

Sorties:

6
répondu Shmuel H. 2017-10-12 12:32:38
    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;
        }
    }
 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();
        }
    }

je ne sais pas si cela convient à votre cas d'utilisation, que vous n'avez pas spécifié la taille des documents que vous essayez d'écrire, mais s'ils sont >3 pages ou vous pouvez les manipuler à moins de 3 pages, il vous permettra pour les convertir en fichiers pdf

1
répondu Bomie 2018-09-01 10:39:40