Windows Docker: Tipps und Tricks zu List & Label

Um List & Label in einem Windows Docker Image ausführen zu können, gibt es ein paar nützliche Tipps & Tricks wie auch Hinweise, die berücksichtigt werden müssten.

Hinweis: Die folgenden Tipps und Tricks beziehen sich auf die Verwendung einer .NET Anwendung - gelten davon aber unabhängig auf für andere Programmierumgebungen wie C++, VCL etc.

Printerless: Kein Drucker(-treiber) verfügbar

In Windows Docker-Images existiert kein nutzbarer Druckertreiber, sodass List & Label im sogenannten Printerless-Mode arbeiten muss - siehe auch List & Label ohne Druckertreiber verwenden (Printerless). Diese Tatsache wird in der Regel von List & Label unter Docker automatisch erkannt und der Modus entsprechend gesetzt. Er kann aber auch explizit über die Eigenschaft Printerless aktiviert werden:

...
LL.Printerless = true;
...

Durch den Printerless-Mode können geringfügige Abweichungen bei der Ausrichtung und Positionierung von Texten entstehen - siehe auch Printerless-Modus: Auswirkungen und Optionen bei der Ausgabe von Texten. Über die Option LlOption.VirtualDeviceScalingOptions kann Einfluss auf die Textausgaben genommen werden - Beispiel:

...
LL.Core.LlSetOption(LlOption.VirtualDeviceScalingOptions, 600);
...

Wichtig: Beachten Sie in diesem Zusammenhang unbedingt den Punkt Fehlende Schriftarten in Windows Docker-Images in diesem Artikel.

Protokollierung

Da in einem Docker-Image das Debwin4-Tool nicht zur Verfügung steht um so bequem wie in einer einfachen Windows Desktop Anwendung die Logausgaben von List & Label aufzuzeichnen, muss die Protokollierung in eine Datei umgeleitet werden. Hierzu stehen die beiden Eigenschaften Debug und DebugLogFilePath zur Verfügung:

...
LL.DebugLogFilePath = logfilePath;
LL.Debug = LlDebug.Enabled | LlDebug.LogToFile;
...

Weitere wichtige Debug-Flags können zusätzlich LlDebug.ObjectStates und LlDebug.ForceSysinfo sein.

Fehlende Schriftarten in Windows Docker-Images

Es gibt nur wenige Windows Docker-Images, bei denen wie gewohnt sämtliche Fonts enthalten sind und für die Ausgabe verwendet werden können. In Server-Core Images steht meist nur die Schriftart Lucon zur Verfügung. Dadurch können aber Ausgaben im Reporting die Verdana, Tahoma, Segoi UI etc. verwenden nicht zufriedenstellend dargestellt werden - siehe auch Fehlende Fonts in den Windows Server Core Docker-Images nachinstallieren.

Ab List & Label in der Version 30 können die Schriftarten direkt über den Designer in die Projektdateien eingebettet werden und ein Nachinstallieren der Schriftarten auf dem Docker-Image ist nicht mehr erforderlich. Frühere Versionen von List & Label müssen jedoch wie hier beschrieben vorgehen.

Für frühere List & Label Version oder wenn die Schriftarten nicht in den Projektdateien eingebettet sind, ist es essentiell wichtig, dass die fehlenden Schriftarten nicht nur auf dem Docker-Image durch einfaches Kopieren bereitgestellt werden - Auszug aus dem Dockerfile:

...
WORKDIR /src
#Copy the fonts to docker
COPY ["/fonts/", "/fonts/"]
...

Die kopierten Schriftarten müssen auch dem ausführenden Prozess mitgeteilt werden - noch bevor das List & Label Objekt erzeugt wird! - was zur Laufzeit über die Windows API AddFontResource() umgesetzt werden kann:

internal static class NativeMethods
{
    [DllImport("gdi32.dll")]
    internal static extern int AddFontResource
        (
        string lpFilename
        );
}

// ...

string[] files = Directory.GetFiles(Path.Combine(appPath, "Fonts"));
foreach(string fileName in files)
{
    NativeMethods.AddFontResource(fileName);
}

Hinweis: Bitte beim Kopieren und Bereitstellen der Schriftarten im Docker-Image ggf. die entsprechenden Lizenzbestimmungen des Herstellers für die Schriftarten berücksichtigen.

Unterschiedliche Maßsysteme

Die Druckvorlagen werden bereits im Vorfeld auf einem lokalen Windows-System über den Designer oder den Web Report Designer erstellt, wobei die lokalen Windows-Region Einstellungen berücksichtigt werden. Doch die bereitgestellten Windows Docker-Images basieren alle auf en-US und es kommt daher inch/Zoll als Maßsystem zum Einsatz.

Wenn nun aber das lokale Windows-System für das Erstellen der Druckvorlagen im Designer ein de-DE System ist und mm/Millimeter als Maßeinheit verwendet und die Druckvorlage in einem Docker-Image mit en-US verwendet wird, kann es zu unerwarteten Endlosschleifen und fehlerhaften Darstellungen im Bericht kommen, da en-US im Standard mit inch/Zoll anstelle von mm/Millimeter rechnet - siehe auch Verwendung von List & Label Druckvorlagen unter gemischten Maßsystemen. Daher sollte man bei der Verwendung für Positions- und Dimensionsangaben mit Formeln von Objekten im Designer die Designer-Funktion UnitFromSCM verwenden. Über die Eigenschaft Unit kann das Maßsystem vom List & Label bestimmt werden:

...
LL.Unit = LlUnits.Millimeter_1_1000;
...

.NET Grundgerüst:

Zusammengefasst eignet sich mit den obigen Informationen das folgende Code-Snippet als gute Ausgangslage:

internal static class NativeMethods
{
    [DllImport("gdi32.dll")]
    internal static extern int AddFontResource
        (
        string lpFilename
        );
}

// ...

// installing fonts to current process
string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string[] files = Directory.GetFiles(Path.Combine(appPath, "Fonts"));
foreach (string fileName in files)
    NativeMethods.AddFontResource(fileName);

using (ListLabel LL = new ListLabel())
{
    //LL.LicensingInfo = "";

    // define printerless-mode
    LL.Printerless = true;

    // define scaling value for text rendering
    LL.Core.LlSetOption(LlOption.VirtualDeviceScalingOptions, 600);

    // define path for logfile and delete old one
    string logfilePath = Path.Combine(appPath, "debug.log");
    if (File.Exists(logfilePath))
        File.Delete(logfilePath);

    // enable debnugging into logfile with helpful options
    LL.DebugLogFilePath = logfilePath;
    LL.Debug = LlDebug.Enabled | LlDebug.LogToFile |
         LlDebug.ObjectStates | LlDebug.ForceSysinfo;

    // define the measurement system
    LL.Unit = LlUnits.Millimeter_1_1000;

    // define additional options, the dada source,
    // project-file/repository, export parameters etc.
    string profectFile = Path.Combine(appPath, "myProject.lst");
    string exportFile = Path.Combine(appPath, "export.ll");

    ExportConfiguration exportPreview = 
        new ExportConfiguration(LlExportTarget.Preview, exportFile, profectFile);

    LL.Export(exportPreview);
}
...