C'était une telle douleur, pas étonnant que toutes les solutions tierces facturent 500 dollars par développeur.
La bonne nouvelle est que le Le SDK Open XML a récemment ajouté la prise en charge de la norme .Net. donc on dirait que vous avez de la chance avec la .docx
format.
Mauvaise nouvelle au moment présent il n'y a pas beaucoup de choix pour les bibliothèques de génération de PDF sur .NET Core. Puisque vous ne semblez pas vouloir payer pour une bibliothèque et que vous ne pouvez pas légalement utiliser un service tiers, nous n'avons pas d'autre choix que de créer la nôtre.
Le principal problème est de transformer le contenu du document Word en PDF. L'une des méthodes les plus courantes consiste à lire le document Word en HTML et à l'exporter en PDF. C'était difficile à trouver, mais il y a une version .Net Core de l'OpenXMLSDK. PowerTools qui prend en charge la transformation de Docx en HTML. La Pull Request est "sur le point d'être acceptée", vous pouvez l'obtenir ici :
https://github.com/OfficeDev/Open-Xml-PowerTools/tree/abfbaac510d0d60e2f492503c60ef897247716cf
Maintenant que nous pouvons extraire le contenu du document en HTML, nous devons le convertir en PDF. Il existe quelques bibliothèques pour convertir le HTML en PDF, par exemple DinkToPdf est une enveloppe multiplateforme autour de la bibliothèque Webkit HTML to PDF libwkhtmltox.
Je pensais que DinkToPdf était meilleur que https://code.msdn.microsoft.com/How-to-export-HTML-to-PDF-c5afd0ce
Docx vers HTML
Pour résumer, téléchargez le projet OpenXMLSDK-PowerTools .Net Core et construisez-le (uniquement OpenXMLPowerTools.Core et OpenXMLPowerTools.Core.Example - ignorez les autres projets). Définissez OpenXMLPowerTools.Core.Example comme projet StartUp. Exécutez le projet de la 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();
Assurez-vous que le fichier test.docx est un document Word valide contenant du texte, sinon vous risquez d'obtenir une erreur :
Le paquet spécifié n'est pas valide. La partie principale est manquante.
Si vous exécutez le projet, vous verrez que le HTML ressemble presque exactement au contenu du document Word :
Cependant, si vous essayez un document Word contenant des images ou des liens, vous remarquerez qu'ils sont manquants ou brisés.
Cet article de CodeProject aborde ces questions : https://www.codeproject.com/Articles/1162184/Csharp-Docx-to-HTML-to-Docx
J'ai dû changer le static Uri FixUri(string brokenUri)
pour retourner un Uri
et j'ai ajouté des 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";
}
}
Vous pouvez avoir besoin du paquet NuGet System.Drawing.Common pour utiliser ImageFormat.
Maintenant, nous pouvons obtenir des images :
Si vous souhaitez uniquement afficher des fichiers Word .docx dans un navigateur Web, il est préférable de ne pas convertir le HTML en PDF, car cela augmente considérablement la bande passante. Vous pouvez stocker le HTML dans un système de fichiers, dans le nuage ou dans un dB en utilisant une technologie VPP.
HTML à PDF
La prochaine chose que nous devons faire est de passer le HTML à DinkToPdf. Téléchargez la solution DinkToPdf (90 MB). Construisez la solution - il faudra un certain temps pour que tous les paquets soient restaurés et que la solution soit compilée.
IMPORTANT :
La bibliothèque DinkToPdf nécessite les fichiers libwkhtmltox.so et libwkhtmltox.dll dans la racine de votre projet si vous voulez fonctionner sous Linux et Windows. Il y a aussi un fichier libwkhtmltox.dylib pour Mac si vous en avez besoin.
Ces DLLs se trouvent dans le dossier v0.12.4. En fonction de votre PC, 32 ou 64 bit, copiez les 3 fichiers dans le 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. La bibliothèque libwkhtmltox.so en dépend.
Définissez DinkToPfd.TestConsoleApp comme projet StartUp et modifiez le fichier Program.cs pour lire le htmlContent du fichier HTML enregistré avec Open-Xml-PowerTools au lieu du texte 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:\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 du Docx par rapport au PDF est assez impressionnant et je doute que beaucoup de gens puissent relever de nombreuses différences (surtout s'ils ne voient jamais l'original) :
Ps. Je réalise que tu voulais convertir les deux .doc
y .docx
en PDF. Je vous suggère de créer vous-même un service pour convertir le format .doc en docx en utilisant une technologie spécifique non serveur de Windows/Microsoft. Le format doc est binaire et n'est pas destiné à être utilisé par les utilisateurs. automatisation de la bureautique côté serveur .
7 votes
C'est comme demander de convertir de Word en PDF sans l'aide de Microsoft. C'est théoriquement possible, mais Word est une application tellement énorme que dans le cas général, c'est pratiquement impossible, Word reste le meilleur pour cela. Vous pourriez connecter vos applications principales à une boîte Windows dédiée opaque exposant un service de conversion (ne négligez pas les questions de licence). Sinon, si vous limitez vos ambitions de conversion, il existe quelques bibliothèques qui devraient vous aider (aspose, itextsharp, etc.). N'oubliez pas non plus que doc et docx sont des formats fondamentalement très différents et que les solutions peuvent varier en conséquence.
7 votes
@SimonMourier
docx
est (soi-disant) un format ouvert (Microsoft a fait pression pendant des années sur ce point) mais il est assez horrible - sous le capot, c'est juste un tas de fichiers xml dans un zip.doc
est binaire, mais aussi pratiquement inchangé depuis 20 ans et de nombreux analyseurs syntaxiques pour ce format sont déjà disponibles. Office a toujours été une application de bureau et une responsabilité coûteuse sur les serveurs, je ne peux pas être la première ou la seule personne à demander cela.2 votes
@SimonMourier J'ai déjà utilisé Aspose - mon équipe n'a pas été très impressionnée, c'est excessivement cher pour ce qu'il fait et c'est un gros .NET, donc pas utile ici de toute façon. iText est bon pour la manipulation des PDF, mais il est aussi cher alors qu'il y a beaucoup d'API PDF qui sont open source.
2 votes
Eh bien, il semble que vous ayez déjà toutes les réponses ; en effet, vous n'êtes pas le seul à chercher le Saint Graal :-)
1 votes
@SimonMourier Je n'aurais pas mis une prime de 500 si je n'avais pas pensé que c'était un sacré problème :-)
2 votes
Je ne comprends pas vraiment le problème. Il y a beaucoup d'implémentations open source pour ces formats. Vous pouvez par exemple obtenir un binaire de libreoffice et exécuter
soffice --convert-to pdf --nologo name.docx
et vous aurez un fichier pdf.1 votes
@ShmuelH. Je pourrais en effet - vous le faites son facile. Pourquoi ne pas faire l'effort supplémentaire de le mettre dans une réponse ? Vous ne pouvez pas gagner de points ou de primes pour les commentaires.
0 votes
@Keith Je voulais m'assurer que je ne manquais pas quelque chose. Merci.
2 votes
Libreoffice est terrible pour la précision. ce n'est que lorsque le client se plaint que vous vous rendez compte de ce fait. j'ai vu une douzaine de convertisseurs et ils échouent tous par rapport à la simple sauvegarde en pdf dans word. les milliers de dollars que les tiers facturent sont pathétiques pour la précision qu'ils donnent. complètement inutilisable.