HaikoFalk
Software Engineer

PDF Thumbnail beim Publish erstellen


Es ergab sich die Anforderung, dass automatisch beim publish eines bestimmten Items, das mit einem Media Item verlinkt ist, von genau diesem Media Item ein Thumbnail erzeugt werden soll, wenn es sich um ein PDF handelt. Das Thumbnail sollte immer von der ersten Seite sein. Ziel war es, einen Wiedererkennungswert zu schaffen.

In den Publish Prozess hängen

Sich in die Pipeline von Sitecore zu hängen, die beim publish eines Items abgearbeitet wird, ist eine recht einfache Aufgabe:

In der Web.config unter pipelines.publishItem wird ein weiter Prozessor nach dem <processor type="Sitecore.Publishing.Pipelines.PublishItem.MoveItems, Sitecore.Kernel"/> definiert.

Der Prozess

Der neue Prozessor erbt von PublishItemProcessor und implementiert die Methode:

public override void Process(PublishItemContext context)

Aus dem Context bekommt man unter anderem das Item, welches gepublished wird und die Sprache in der gepublished wird:

Language language = context.PublishOptions.Language;
Item item = context.PublishOptions.SourceDatabase.GetItem(context.ItemId, language);

Wenn es sich also um das Item vom gewünscht Typ handelt und der MediaItem.MimeType ein application/pdf ist wird für unsere Lösung das PDF local im FileSystem gespeichert. Dank der iTextSharp.dll kann man hier auch direkt nur die erste Seite speichern:

Stream stream = mediaItem.GetMediaStream();
PdfReader reader = new PdfReader(stream);
Document document = new Document();
document.SetPageSize(reader.GetPageSize(1));
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(filePath, FileMode.Create));
document.Open();
PdfContentByte cb = writer.DirectContent;
document.NewPage();
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
document.Close();

Diese ganze Logik haben wir in einen eigenen Thread ausgelagert, da der Publish prozess sonst unnötig verzögert wird.
Anschließend wird der Pfad des gespeicherten einseitigen PDFs an einen Webservice übergeben, der das Bild erstellt und den Pfad des Thumbnails zurückgibt. Für diese Lösung haben wir uns entschieden um das ganze wiederverwertbar zu machen.

Der Webservice generiert mit Hilfe des Ghostscript Wrappers für c# von Matthew Ephraim das Bild

PdfReader reader = new PdfReader(pdfPath);
GhostscriptSettings s = new GhostscriptSettings();
s.Device = GetGhostscriptSetting(codex);
s.Page.Start = 1;
s.Page.End = 1;
s.Resolution = new System.Drawing.Size((Convert.ToInt32(reader.GetPageSize(1).Width) / 2), Convert.ToInt32(reader.GetPageSize(1).Height) / 2);
GhostscriptSharp.Settings.GhostscriptPageSize pageSize = new GhostscriptSharp.Settings.GhostscriptPageSize();
pageSize.Native = GhostscriptSharp.Settings.GhostscriptPageSizes.a4;
s.Size = pageSize;
GhostscriptWrapper.GenerateOutput(pdfPath, fileThumbnailPath, s);

Zum anpassend der Bildgröße findet man zahlreiche Beispiele im Internet. Der Pfad vom fertigen Thumbnail wird vom Webservice zurückgegeben.

Das Thumbnail kann im FileSystem richtig abgelegt werden oder in Sitecore in die Media Library hinzugefügt werden.

Man sollte nur nicht vergessen die alten Dateien, die nicht mehr gebraucht werden zu löschen.

Mögliche Fehler

Der Aufruf des WebService kann natürlich in einen TimeOut laufen. Gegebenenfalls sollte man hier die Zeiten anpassen.

Ein weit häufigerer Fehler war bisher, dass die PDF-Dateien geschützt waren (gegen Kopieren zum Beispiel), so das es zu einem Fehler von Ghostscript kam.

Ein weiterer Fehler, den man auf jedenfall abfangen sollte ist, das die Datei gelockt ist. Eine einfache Hilfsmethode ist:

private bool IsFileLocked(string filePath)
{ FileStream stream = null; if (!File.Exists(filePath)) return false; try { stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); } catch (IOException) { //the file is unavailable because it is: //still being written to
//or
//or being processed by another thread does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}

 

Webservice Konfiguration

Die Konfiguration des Webservice hat keinerlei Besonderheiten. Die Web.Config des sitecore-Projekts bekommt den Endpunkt

<system.serviceModel>
<client> <endpoint address="http://localhost:59191/PdfThumbnailGenerator.svc" binding="basicHttpBinding" contract="Uponor.Web.ServiceContracts.IPdfThumbnailGenerator" /> </client> </system.serviceModel>

Die Service Contracts sind als eingenes Projekt in der Solution, und enthält das Interface und die Client-definition.

 

Viel Spaß beim Nachbaunen
Haiko

Kommentare
Es wurden noch keine Kommentare zu diesem Eintrag geschrieben.
Kommentar hinzufügen
Vor und Zuname
E-Mail
E-Mail bei weiteren Kommentaren
Mein Kommentar