public override void FinalizeReferenceLoad()
        {
            if (!m_bCacheUpdated || !CardMakerSettings.EnableGoogleCache)
            {
                return;
            }

            List <GoogleCacheItem> listCacheItems = new List <GoogleCacheItem>();

            foreach (var zPair in m_dictionaryDataCache)
            {
                listCacheItems.Add(new GoogleCacheItem()
                {
                    Reference = zPair.Key,
                    Data      = zPair.Value
                });
            }
            var sLocalCacheFile = Path.Combine(CardMakerInstance.StartupPath, CardMakerConstants.GOOGLE_CACHE_FILE);

            if (!SerializationUtils.SerializeToXmlFile(
                    sLocalCacheFile,
                    listCacheItems,
                    CardMakerConstants.XML_ENCODING))
            {
                ProgressReporter.AddIssue("Failed to write cache file: {0}".FormatString(sLocalCacheFile));
            }
        }
        private void LoadCache()
        {
            if (!CardMakerSettings.EnableGoogleCache)
            {
                return;
            }
            var sLocalCacheFile = Path.Combine(CardMakerInstance.StartupPath, CardMakerConstants.GOOGLE_CACHE_FILE);

            List <GoogleCacheItem> listCacheItems = null;

            if (SerializationUtils.DeserializeFromXmlFile(
                    sLocalCacheFile,
                    CardMakerConstants.XML_ENCODING,
                    ref listCacheItems))
            {
                foreach (var zCacheItem in listCacheItems)
                {
                    m_dictionaryDataCache.Add(zCacheItem.Reference, zCacheItem.Data);
                }
            }
            else
            {
                ProgressReporter.AddIssue("Failed to read cache file: {0}".FormatString(sLocalCacheFile));
            }
        }
예제 #3
0
 public PdfSharpExporter(int[] arrayLayoutIndices, string sExportFile, string sPageOrientation) : base(arrayLayoutIndices)
 {
     m_sExportFile = sExportFile;
     try
     {
         m_ePageOrientation = (PageOrientation)Enum.Parse(typeof(PageOrientation), sPageOrientation);
     }
     catch (Exception)
     {
         ProgressReporter.AddIssue(sPageOrientation + " is an unknown page orientation.");
     }
     m_zDocument = new PdfDocument();
 }
예제 #4
0
        /// <summary>
        /// Shows the read-only error (assumes the file is open in a viewer)
        /// </summary>
        private void DisplayError(string extraMessage = "")
        {
            var sMsg = "{0}The destination file may be open in a PDF viewer. Please close it before exporting."
                       .FormatString(
                (string.IsNullOrWhiteSpace(extraMessage)
                        ? ""
                        : extraMessage + " -- "
                ));

            if (null != CardMakerInstance.ApplicationForm)
            {
                CardMakerInstance.ApplicationForm.InvokeAction(() =>
                {
                    MessageBox.Show(CardMakerInstance.ApplicationForm, sMsg, "PDF Write Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                });
            }
            else
            {
                ProgressReporter.AddIssue(sMsg);
            }
        }
예제 #5
0
        public void GetData(string sPath, List <List <string> > listData, bool bLogNotFound, int nStartIdx, string nameAppend = "")
        {
            CSVFile zCSVParser    = null;
            var     bTryCopy      = false;
            var     sCombinedPath =
                Path.GetDirectoryName(sPath) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(sPath) + nameAppend + Path.GetExtension(sPath);

            if (!File.Exists(sCombinedPath))
            {
                if (bLogNotFound)
                {
                    var sMsg = "CSV File not found: " + sCombinedPath;
                    ProgressReporter.AddIssue(sMsg);
                    IssueManager.Instance.FireAddIssueEvent(sMsg);
                }
                return;
            }
            try
            {
                zCSVParser = new CSVFile(sCombinedPath, Encoding.UTF8);
            }
            catch (Exception)
            {
                bTryCopy = true;
            }

            // This is a ridiculous hack to work around excel locking the file from even being read
            if (bTryCopy)
            {
                var sTmpFile = sCombinedPath + "." + DateTime.Now.Millisecond + ".tmp";
                File.Copy(sCombinedPath, sTmpFile);
                zCSVParser = new CSVFile(sTmpFile, Encoding.UTF8);
                File.Delete(sTmpFile); // remove the temp file
            }

            for (var nLine = nStartIdx; nLine < zCSVParser.Rows; nLine++)
            {
                listData.Add(zCSVParser.GetRow(nLine));
            }
        }
        public void GetData(GoogleSpreadsheetReference zReference, List <List <string> > listData, bool bRemoveFirstRow, string sNameAppend = "")
        {
            var sCacheKey = GetCacheKey(zReference.generateFullReference(), sNameAppend);
            List <List <string> > listCacheData;

            if (!CardMakerInstance.ForceDataCacheRefresh && m_dictionaryDataCache.TryGetValue(sCacheKey, out listCacheData))
            {
                ProgressReporter.AddIssue("Loading {0} from local cache".FormatString(sCacheKey));
                listData.AddRange(listCacheData);
                return;
            }

            var sSpreadsheetName = zReference.SpreadsheetName;
            var sSheetName       = zReference.SheetName + sNameAppend;

            var bAuthorizationError = false;
            var bError = false;

            List <List <string> > listGoogleData = null;

            try
            {
                var zGoogleSpreadsheet = new GoogleSpreadsheet(CardMakerInstance.GoogleInitializerFactory);
                if (string.IsNullOrWhiteSpace(zReference.SpreadsheetId))
                {
                    ProgressReporter.AddIssue("WARNING: The reference {0}.{1} is missing the Spreadsheet ID. Please reconfigure this reference."
                                              .FormatString(zReference.SpreadsheetName, zReference.SheetName));
                    listGoogleData = zGoogleSpreadsheet.GetSheetContentsBySpreadsheetName(sSpreadsheetName, sSheetName);
                }
                else
                {
                    listGoogleData = zGoogleSpreadsheet.GetSheetContentsBySpreadsheetId(zReference.SpreadsheetId, sSheetName);
                }

                // blank data just means an empty or non-existent sheet (generally okay)
                if (listGoogleData == null)
                {
                    listGoogleData = new List <List <string> >();
                }
            }
            catch (GoogleApiException e)
            {
                ProgressReporter.AddIssue("Google Spreadsheet access exception: " + e.Message);
                bAuthorizationError = GoogleApi.IsAuthorizationError(e);
            }
            catch (Exception e)
            {
                ProgressReporter.AddIssue("General exception: " + e.Message);
                listGoogleData = null;
                bError         = true;
            }
            if (bAuthorizationError || bError || listGoogleData == null)
            {
                ProgressReporter.AddIssue("Failed to load any data from Google Spreadsheet." + "[" + sSpreadsheetName + "," + sSheetName + "]" + (bAuthorizationError ? " Google reported a problem with your credentials." : String.Empty));
            }
            else
            {
                if (bRemoveFirstRow && listGoogleData.Count > 0)
                {
                    listGoogleData.RemoveAt(0);
                }

                listData.AddRange(listGoogleData);
                if (m_dictionaryDataCache.ContainsKey(sCacheKey))
                {
                    m_dictionaryDataCache.Remove(sCacheKey);
                }
                m_dictionaryDataCache.Add(sCacheKey, listGoogleData);
                m_bCacheUpdated = true;
            }
        }
예제 #7
0
        public override void ExportThread()
        {
            if (File.Exists(m_sExportFile))
            {
                try
                {
                    File.Delete(m_sExportFile);
                }
                catch (Exception)
                {
                    ProgressReporter.AddIssue("Failed to delete PDF before export: {0}".FormatString(m_sExportFile));
                }

                if (File.Exists(m_sExportFile))
                {
                    DisplayError();
                    ProgressReporter.Shutdown();
                    return;
                }
            }

#if !MONO_BUILD
            Bitmap zBuffer = null;
#endif

            var progressLayoutIdx = ProgressReporter.GetProgressIndex(ProgressName.LAYOUT);
            var progressCardIdx   = ProgressReporter.GetProgressIndex(ProgressName.CARD);

            ProgressReporter.ProgressReset(progressLayoutIdx, 0, ExportLayoutIndices.Length, 0);

            // always add a new page initially (necessary for ConfigurePointSizes)
            AddInitialPage();

            Type zLastExporterType = null;

            foreach (var nIdx in ExportLayoutIndices)
            {
                ChangeExportLayoutIndex(nIdx);
                m_zExportData.Deck = CurrentDeck;
                if (CurrentDeck.EmptyReference)
                {
                    // empty reference layouts are not exported
                    ProgressReporter.ProgressStep(progressLayoutIdx);
                    continue;
                }

                ProgressReporter.ProgressReset(progressCardIdx, 0, CurrentDeck.CardCount, 0);

                var rectCrop = CurrentDeck.CardLayout.getExportCropDefinition();

                // each layout has its own row export style (potentially)
                var zRowExporter = GetRowExporter();

                // necessary tracking for which index of the layout is to be exported (this is NOT necessarily the index of the card due to the page back functionality)
                var nNextExportIndex = 0;

                // reconfigure the page per layout
                ConfigurePointSizes(CurrentDeck.CardLayout, rectCrop);

                if (nIdx == 0)
                {
                    // only the page has been setup at this point, not the x-position
                    zRowExporter.SetupNewRowXPosition(nNextExportIndex);
                }
                else
                {
                    // after the first layout always evaluate whether a new page should be added
                    // if the exporter type changes start a new page
                    if (CardMakerSettings.PrintLayoutsOnNewPage || zLastExporterType != zRowExporter.GetType())
                    {
                        AddPage(zRowExporter, nNextExportIndex);
                    }
                    else if (zRowExporter.IsLayoutForcedToNewRow() || zRowExporter.IsRowFull())
                    {
                        MoveToNextRow(zRowExporter, nNextExportIndex);
                    }
                }

                zLastExporterType = zRowExporter.GetType();

                // When moving to the next row it should be a completely empty row. In the case of nesting only 2 layouts deep is supported at this
                // time - the first layout and one additional. Also note this is applied AFTER the above decisions above about moving to the next row.
                m_zExportData.NextRowYAdjust = Math.Max(m_zExportData.NextRowYAdjust, m_zExportData.LayoutPointHeight + m_zExportData.BufferY);

#if !MONO_BUILD
                zBuffer?.Dispose();
                zBuffer = createExportBuffer(CurrentDeck.CardLayout, rectCrop);

                var fOriginalXDpi = zBuffer.HorizontalResolution;
                var fOriginalYDpi = zBuffer.VerticalResolution;
#endif

                foreach (var nCardIdx in GetExportIndices())
                {
                    CurrentDeck.CardPrintIndex = nCardIdx;

#if MONO_BUILD
                    // mono build won't support the optimization so re-create the buffer
                    Bitmap zBuffer = createExportBuffer(CurrentDeck.CardLayout, rectCrop);
#else
                    // minor optimization, reuse the same bitmap (for drawing sake the DPI has to be reset)
                    zBuffer.SetResolution(fOriginalXDpi, fOriginalYDpi);
#endif
                    // Draw the image into the buffer
                    if (nCardIdx == -1)
                    {
                        Graphics.FromImage(zBuffer).FillRectangle(Brushes.White, 0, 0, zBuffer.Width, zBuffer.Height);
                        // note: some oddities were observed where the buffer was not flood filling
                    }
                    else
                    {
                        CardRenderer.DrawPrintLineToGraphics(Graphics.FromImage(zBuffer), -rectCrop.X, -rectCrop.Y, true);
                        // if cropping the border needs to be drawn to the cropped size
                        if (rectCrop != Rectangle.Empty)
                        {
                            CardRenderer.DrawBorder(Graphics.FromImage(zBuffer), 0, 0, zBuffer.Width, zBuffer.Height, CurrentDeck.CardLayout, true);
                        }
                    }

                    // apply any export rotation
                    ProcessRotateExport(zBuffer, CurrentDeck.CardLayout, false);

                    // before rendering to the PDF bump the DPI to the desired value
                    zBuffer.SetResolution(CurrentDeck.CardLayout.dpi, CurrentDeck.CardLayout.dpi);

                    var xImage = XImage.FromGdiPlusImage(zBuffer);

                    // before drawing make sure there is space (will move to next row/page)
                    EvaluateDrawLocation(nNextExportIndex, zRowExporter);

                    m_zPageGfx.DrawImage(xImage, m_zExportData.DrawX, m_zExportData.DrawY);

                    // any next row movement is dealt with independent of this call
                    zRowExporter.MoveXToNextColumnPosition();

                    // undo any export rotation
                    ProcessRotateExport(zBuffer, CurrentDeck.CardLayout, true);

                    nNextExportIndex++;

                    ProgressReporter.ProgressStep(progressCardIdx);
                }
                ProgressReporter.ProgressStep(progressLayoutIdx);
            }

#if !MONO_BUILD
            zBuffer?.Dispose();
#endif

            try
            {
                m_zDocument.Save(m_sExportFile);
                ProgressReporter.ThreadSuccess = true;
            }
            catch (Exception ex)
            {
                DisplayError(ex.Message);
                ProgressReporter.ThreadSuccess = false;
            }

            ProgressReporter.Shutdown();
        }
예제 #8
0
        public override void ExportThread()
        {
            if (File.Exists(m_sExportFile))
            {
                try
                {
                    File.Delete(m_sExportFile);
                }
                catch (Exception)
                {
                    ProgressReporter.AddIssue("Failed to delete PDF before export: {0}".FormatString(m_sExportFile));
                }

                if (File.Exists(m_sExportFile))
                {
                    DisplayError();
                    ProgressReporter.Shutdown();
                    return;
                }
            }

#if !MONO_BUILD
            Bitmap zBuffer = null;
#endif

            var progressLayoutIdx = ProgressReporter.GetProgressIndex(ProgressName.LAYOUT);
            var progressCardIdx   = ProgressReporter.GetProgressIndex(ProgressName.CARD);

            ProgressReporter.ProgressReset(progressLayoutIdx, 0, ExportLayoutIndices.Length, 0);
            foreach (var nIdx in ExportLayoutIndices)
            {
                ChangeExportLayoutIndex(nIdx);
                if (CurrentDeck.EmptyReference)
                {
                    // empty reference layouts are not exported
                    ProgressReporter.ProgressStep(progressLayoutIdx);
                    continue;
                }

                ProgressReporter.ProgressReset(progressCardIdx, 0, CurrentDeck.CardCount, 0);

                var rectCrop = CurrentDeck.CardLayout.getExportCropDefinition();

                ConfigurePointSizes(CurrentDeck.CardLayout, rectCrop);

                // necessary tracking for which index of the layout is to be exported (this is NOT necessarily the index of the card due to the page back functionality)
                var nNextExportIndex = 0;

                if (0 < nIdx)
                {
                    if (CardMakerSettings.PrintLayoutsOnNewPage)
                    {
                        AddPage();
                    }

                    if (CardMakerSettings.PrintAutoHorizontalCenter ||
                        (m_dDrawX + m_dLayoutPointWidth > m_dPageMarginEndX)) // this is the case where a layout won't fit in the remaining space of the row
                    {
                        MoveToNextRow(nNextExportIndex);
                    }
                }

                // should be adjusted after the above move to next row
                m_dNextRowYAdjust = Math.Max(m_dNextRowYAdjust, m_dLayoutPointHeight + m_dBufferY);

                if (CardMakerSettings.PrintAutoHorizontalCenter)
                {
                    CenterLayoutPositionOnNewRow(nNextExportIndex);
                }

#if !MONO_BUILD
                zBuffer?.Dispose();
                zBuffer = createExportBuffer(CurrentDeck.CardLayout, rectCrop);

                float fOriginalXDpi = zBuffer.HorizontalResolution;
                float fOriginalYDpi = zBuffer.VerticalResolution;
#endif

                foreach (var nCardIdx in GetExportIndices())
                {
                    CurrentDeck.ResetDeckCache();

                    CurrentDeck.CardPrintIndex = nCardIdx;

#if MONO_BUILD
                    // mono build won't support the optimization so re-create the buffer
                    Bitmap zBuffer = createExportBuffer(CurrentDeck.CardLayout, rectCrop);
#endif

#if !MONO_BUILD
                    // minor optimization, reuse the same bitmap (for drawing sake the DPI has to be reset)
                    zBuffer.SetResolution(fOriginalXDpi, fOriginalYDpi);
#endif
                    if (nCardIdx == -1)
                    {
                        Graphics.FromImage(zBuffer).FillRectangle(Brushes.White, 0, 0, zBuffer.Width, zBuffer.Height);
                        // note: some oddities were observed where the buffer was not flood filling
                    }
                    else
                    {
                        CardRenderer.DrawPrintLineToGraphics(Graphics.FromImage(zBuffer), -rectCrop.X, -rectCrop.Y, true);
                        // if cropping the border needs to be drawn to the cropped size
                        if (rectCrop != Rectangle.Empty)
                        {
                            CardRenderer.DrawBorder(Graphics.FromImage(zBuffer), 0, 0, zBuffer.Width, zBuffer.Height, CurrentDeck.CardLayout, true);
                        }
                    }

                    // apply any export rotation
                    ProcessRotateExport(zBuffer, CurrentDeck.CardLayout, false);

                    // before rendering to the PDF bump the DPI to the desired value
                    zBuffer.SetResolution(CurrentDeck.CardLayout.dpi, CurrentDeck.CardLayout.dpi);

                    var xImage = XImage.FromGdiPlusImage(zBuffer);

                    EvaluatePagePosition(nNextExportIndex);

                    m_zPageGfx.DrawImage(xImage, m_dDrawX, m_dDrawY);

                    MoveToNextColumnPosition();

                    // undo any export rotation
                    ProcessRotateExport(zBuffer, CurrentDeck.CardLayout, true);

                    nNextExportIndex++;

                    ProgressReporter.ProgressStep(progressCardIdx);
                }
                ProgressReporter.ProgressStep(progressLayoutIdx);
            }

#if !MONO_BUILD
            zBuffer?.Dispose();
#endif

            try
            {
                m_zDocument.Save(m_sExportFile);
                ProgressReporter.ThreadSuccess = true;
            }
            catch (Exception ex)
            {
                DisplayError(ex.Message);
                ProgressReporter.ThreadSuccess = false;
            }

            ProgressReporter.Shutdown();
        }
예제 #9
0
 public void AddIssue(string sIssue)
 {
     ProgressReporter.AddIssue(sIssue);
 }
예제 #10
0
        public override void ExportThread()
        {
            var progressLayoutIdx = ProgressReporter.GetProgressIndex(ProgressName.LAYOUT);
            var progressCardIdx   = ProgressReporter.GetProgressIndex(ProgressName.CARD);

            ProgressReporter.ProgressReset(progressLayoutIdx, 0, ExportLayoutIndices.Length, 0);
            foreach (var nIdx in ExportLayoutIndices)
            {
                ChangeExportLayoutIndex(nIdx);
                if (CurrentDeck.EmptyReference)
                {
                    // empty reference layouts are not exported
                    ProgressReporter.ProgressStep(progressLayoutIdx);
                    continue;
                }
                var nPadSize = CurrentDeck.CardCount.ToString(CultureInfo.InvariantCulture).Length;
                ProgressReporter.ProgressReset(progressCardIdx, 0, CurrentDeck.CardCount, 0);

                var exportWidth = CurrentDeck.CardLayout.exportWidth == 0
                    ? CurrentDeck.CardLayout.width : CurrentDeck.CardLayout.exportWidth;

                var exportHeight = CurrentDeck.CardLayout.exportHeight == 0
                    ? CurrentDeck.CardLayout.height : CurrentDeck.CardLayout.exportHeight;

                if (CurrentDeck.CardLayout.width > exportWidth ||
                    CurrentDeck.CardLayout.height > exportHeight)
                {
                    Logger.AddLogLine(
                        $"ERROR: Layout: [{CurrentDeck.CardLayout.Name}] exportWidth and/or exportHeight too small! (Skipping export)");
                    continue;
                }

                UpdateBufferBitmap(exportWidth, exportHeight);
                var zGraphics        = Graphics.FromImage(m_zExportCardBuffer);
                var arrayCardIndices = GetCardIndicesArray(CurrentDeck);
                for (var nCardArrayIdx = 0; nCardArrayIdx < arrayCardIndices.Length; nCardArrayIdx++)
                {
                    var nCardId = arrayCardIndices[nCardArrayIdx];
                    var nX      = 0;
                    var nY      = 0;
                    var nCardsExportedInImage = 0;
                    zGraphics.Clear(CurrentDeck.CardLayout.exportTransparentBackground ?
                                    CardMakerConstants.NoColor :
                                    Color.White);
                    do
                    {
                        CurrentDeck.ResetDeckCache();
                        // HACK - the printcard index is 0 based but all other uses of nCardId are 1 based (so ++ it!)
                        CurrentDeck.CardPrintIndex = nCardId++;
                        nCardsExportedInImage++;
                        CardRenderer.DrawPrintLineToGraphics(zGraphics, nX, nY, !CurrentDeck.CardLayout.exportTransparentBackground);
                        m_zExportCardBuffer.SetResolution(CurrentDeck.CardLayout.dpi, CurrentDeck.CardLayout.dpi);

                        ProgressReporter.ProgressStep(progressCardIdx);

                        int nMoveCount = 1;
                        if (m_nSkipStitchIndex > 0)
                        {
                            var x = ((nCardsExportedInImage + 1) % m_nSkipStitchIndex);
                            if (x == 0)
                            {
                                // shift forward an extra spot to ignore the dummy index
                                nMoveCount = 2;
                            }
                        }

                        var bOutOfSpace = false;
                        for (int nShift = 0; nShift < nMoveCount; nShift++)
                        {
                            nX += CurrentDeck.CardLayout.width + CurrentDeck.CardLayout.buffer;
                            if (nX + CurrentDeck.CardLayout.width > exportWidth)
                            {
                                nX  = 0;
                                nY += CurrentDeck.CardLayout.height + CurrentDeck.CardLayout.buffer;
                            }
                            if (nY + CurrentDeck.CardLayout.height > exportHeight)
                            {
                                // no more space
                                bOutOfSpace = true;
                                break;
                            }
                        }

                        if (bOutOfSpace)
                        {
                            break;
                        }
                    } while (nCardArrayIdx < CurrentDeck.CardCount);

                    string sFileName;

                    // NOTE: nCardId at this point is 1 more than the actual index ... how convenient for export file names...

                    if (!string.IsNullOrEmpty(m_sOverrideStringFormat))
                    {
                        // check for the super override
                        sFileName = CurrentDeck.TranslateFileNameString(m_sOverrideStringFormat, nCardId, nPadSize);
                    }
                    else if (!string.IsNullOrEmpty(CurrentDeck.CardLayout.exportNameFormat))
                    {
                        // check for the per layout override
                        sFileName = CurrentDeck.TranslateFileNameString(CurrentDeck.CardLayout.exportNameFormat, nCardId, nPadSize);
                    }
                    else // default
                    {
                        sFileName = CurrentDeck.CardLayout.Name + "_" + (nCardId).ToString(CultureInfo.InvariantCulture).PadLeft(nPadSize, '0');
                    }
                    try
                    {
                        ProcessRotateExport(m_zExportCardBuffer, CurrentDeck.CardLayout, false);
                        m_zExportCardBuffer.Save(
                            m_sExportFolder + sFileName +
                            "." + m_eImageFormat.ToString().ToLower(),
                            m_eImageFormat);
                        ProcessRotateExport(m_zExportCardBuffer, CurrentDeck.CardLayout, true);
                    }
                    catch (Exception ex)
                    {
                        ProgressReporter.AddIssue("Invalid Filename or IO error: " + sFileName + " :: " + ex.Message);
                        ProgressReporter.ThreadSuccess = false;
                        ProgressReporter.Shutdown();
                        return;
                    }
                }
                ProgressReporter.ProgressStep(progressLayoutIdx);
            }

            ProgressReporter.ThreadSuccess = true;
            ProgressReporter.Shutdown();
        }
예제 #11
0
        public override void ExportThread()
        {
            var progressLayoutIdx = ProgressReporter.GetProgressIndex(ProgressName.LAYOUT);
            var progressCardIdx   = ProgressReporter.GetProgressIndex(ProgressName.CARD);

            ProgressReporter.ProgressReset(progressLayoutIdx, 0, ExportLayoutIndices.Length, 0);
            ChangeExportLayoutIndex(ExportLayoutIndices[0]);
            var nPadSize = CurrentDeck.CardCount.ToString(CultureInfo.InvariantCulture).Length;

            ProgressReporter.ProgressReset(progressCardIdx, 0, CurrentDeck.CardCount, 0);

            UpdateBufferBitmap(CurrentDeck.CardLayout.width, CurrentDeck.CardLayout.height);

            var zGraphics = Graphics.FromImage(m_zExportCardBuffer);
            var nCardIdx  = m_nImageExportIndex;

            zGraphics.Clear(CurrentDeck.CardLayout.exportTransparentBackground ?
                            CardMakerConstants.NoColor :
                            Color.White);
            CurrentDeck.ResetDeckCache();
            CurrentDeck.CardPrintIndex = nCardIdx++;
            CardRenderer.DrawPrintLineToGraphics(zGraphics, 0, 0, !CurrentDeck.CardLayout.exportTransparentBackground);
            m_zExportCardBuffer.SetResolution(CurrentDeck.CardLayout.dpi, CurrentDeck.CardLayout.dpi);

            ProgressReporter.ProgressStep(progressCardIdx);

            string sFileName;

            // NOTE: nCardIdx at this point is 1 more than the actual index ... how convenient for export file names...

            if (!string.IsNullOrEmpty(m_sOverrideStringFormat))
            {
                // check for the super override
                sFileName = CurrentDeck.TranslateFileNameString(m_sOverrideStringFormat, nCardIdx, nPadSize);
            }
            else if (!string.IsNullOrEmpty(CurrentDeck.CardLayout.exportNameFormat))
            {
                // check for the per layout override
                sFileName = CurrentDeck.TranslateFileNameString(CurrentDeck.CardLayout.exportNameFormat, nCardIdx, nPadSize);
            }
            else // default
            {
                sFileName = CurrentDeck.CardLayout.Name + "_" + (nCardIdx).ToString(CultureInfo.InvariantCulture).PadLeft(nPadSize, '0');
            }
            try
            {
                ProcessRotateExport(m_zExportCardBuffer, CurrentDeck.CardLayout, false);
                m_zExportCardBuffer.Save(
                    m_sExportFolder + sFileName +
                    "." + m_eImageFormat.ToString().ToLower(),
                    m_eImageFormat);
                ProcessRotateExport(m_zExportCardBuffer, CurrentDeck.CardLayout, true);
            }
            catch (Exception e)
            {
                ProgressReporter.AddIssue("Invalid Filename or IO error: {0} {1}".FormatString(sFileName, e.Message));
                ProgressReporter.ThreadSuccess = false;
                ProgressReporter.Shutdown();
                return;
            }

            ProgressReporter.ProgressStep(progressLayoutIdx);

            ProgressReporter.ThreadSuccess = true;
            ProgressReporter.Shutdown();
        }