Пример #1
0
        /// <summary>
        /// Gather all the data for a single output page
        /// </summary>
        private static Result <DocumentPage> PreparePage(TemplatePage pageDef, DataMapper mapper, Dictionary <string, decimal> runningTotals, int pageIndex)
        {
            var docPage = new DocumentPage(pageDef, pageIndex);

            // Draw each box. We sort the boxes (currently by filter type) so running totals can work as expected
            foreach (var boxDef in pageDef.Boxes.Where(HasAValue).OrderBy(OrderBoxes))
            {
                var result = PrepareBox(mapper, boxDef.Value !, runningTotals, pageIndex);
                if (result.IsFailure && boxDef.Value !.IsRequired)
                {
                    return(Result.Failure <DocumentPage>(result.FailureCause));
                }
                if (result.IsSuccess && result.ResultData != null)
                {
                    docPage.DocumentBoxes.Add(boxDef.Key, result.ResultData);
                }
            }

            return(Result.Success(docPage));
        }
Пример #2
0
        /// <summary>
        /// Draw the prepared box to a PDF page
        /// </summary>
        private Result <Nothing> RenderBox(XFont baseFont, KeyValuePair <string, DocumentBox> boxDef, double fx, double fy, XGraphics gfx, DocumentPage pageToRender, int pageIndex, int pageTotal)
        {
            var box  = boxDef.Value !;
            var font = (box.Definition.BoxFontSize != null) ? GetFont(box.Definition.BoxFontSize.Value) : baseFont;


            // boxes are defined in terms of the background image pixels or existing PDF page, so we need to convert
            var boxWidth  = box.Definition.Width * fx;
            var boxHeight = box.Definition.Height * fy;
            var space     = new XRect(box.Definition.Left * fx, box.Definition.Top * fy, boxWidth, boxHeight);

            // Handle the special case of embedding an image
            if (box.BoxType == DocumentBoxType.EmbedJpegImage)
            {
                if (string.IsNullOrWhiteSpace(box.RenderContent !))
                {
                    return(Result.Success());                                               // empty data is considered OK
                }
                try
                {
                    _loadingTimer.Start();
                    var jpegStream = _files.LoadUrl(box.RenderContent);
                    if (jpegStream is null)
                    {
                        return(Result.Success());                    // Embedded images are always considered optional
                    }
                    var img = XImage.FromStream(jpegStream);
                    gfx.DrawImage(img, space);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                    // Nothing. Embedded images are always considered optional
                }
                finally
                {
                    _loadingTimer.Stop();
                }

                return(Result.Success());
            }

            // Read data, or pick a special type
            var str = box.BoxType switch
            {
                DocumentBoxType.Normal => box.RenderContent,

                // For the special values, we might want to re-apply the display format
                DocumentBoxType.PageGenerationDate => TryApplyDisplayFormat(box, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")),
                DocumentBoxType.CurrentPageNumber => TryApplyDisplayFormat(box, (pageIndex + 1).ToString()),
                DocumentBoxType.TotalPageCount => TryApplyDisplayFormat(box, pageTotal.ToString()),
                DocumentBoxType.RepeatingPageNumber => TryApplyDisplayFormat(box, (pageToRender.RepeatIndex + 1).ToString()),
                DocumentBoxType.RepeatingPageTotalCount => TryApplyDisplayFormat(box, pageToRender.RepeatCount.ToString()),

                _ => box.RenderContent
            };

            if (str == null)
            {
                return(Result.Success());             // empty data is considered OK
            }
            var align = MapAlignments(box.Definition);

            RenderTextInBox(font, gfx, box.Definition, fx, fy, str, space, align);
            return(Result.Success());
        }
Пример #3
0
        /// <summary>
        /// Render a prepared page into a PDF document
        /// </summary>
        private Result <Nothing> OutputPage(PdfDocument document, DocumentPage pageToRender, XFont font, PageBacking background, int pageIndex, int pageTotal)
        {
            var pageDef = pageToRender.Definition;

            // If we have a source PDF, and we aren't trying to render a blank page, then import the page
            var shouldCopyPdfPage = background.ExistingPage != null && pageDef.RenderBackground;
            var page = shouldCopyPdfPage ? document.AddPage(background.ExistingPage !) : document.AddPage(/*blank*/);

            // If dimensions are silly, reset to A4
            if (pageDef.WidthMillimetres < 10 || pageDef.WidthMillimetres > 1000)
            {
                pageDef.WidthMillimetres = 210;
            }
            if (pageDef.HeightMillimetres < 10 || pageDef.HeightMillimetres > 1000)
            {
                pageDef.HeightMillimetres = 297;
            }

            // Set the PDF page size (in points under the hood)
            page.Width  = XUnit.FromMillimeter(pageDef.WidthMillimetres);
            page.Height = XUnit.FromMillimeter(pageDef.HeightMillimetres);

            using var gfx = XGraphics.FromPdfPage(page);
            gfx.Save();

            if (shouldCopyPdfPage && background.ExistingPage !.Rotate != 0)
            {
                // Match visual-rotations on page
                var centre       = new XPoint(0, 0);
                var visualRotate = background.ExistingPage !.Rotate % 360; // degrees, spec says it should be a multiple of 90.
                var angle        = 360.0 - visualRotate;
                gfx.RotateAtTransform(angle, centre);

                switch (visualRotate)
                {
                case 90:
                    gfx.TranslateTransform(-page.Height.Point, 0);
                    break;

                case 180:
                    gfx.TranslateTransform(-page.Width.Point, -page.Height.Point);
                    break;

                case 270:
                    gfx.TranslateTransform(-page.Height.Point / 2.0, -page.Height.Point);     // this one is a guess, as I don't have an example
                    break;

                default:
                    throw new Exception("Unhandled visual rotation case");
                }

                if (background.ExistingPage.Orientation == PageOrientation.Landscape)
                {
                    (page.Width, page.Height) = (page.Height, page.Width);
                }
            }

            // Draw background at full page size
            _loadingTimer.Start();
            if (pageDef.RenderBackground && background.BackgroundImage != null)
            {
                var destRect = new XRect(0, 0, (float)page.Width.Point, (float)page.Height.Point);
                gfx.DrawImage(background.BackgroundImage, destRect);
            }
            _loadingTimer.Stop();

            // Do default scaling
            var fx = page.Width.Point / page.Width.Millimeter;
            var fy = page.Height.Point / page.Height.Millimeter;

            // If using an image, work out the bitmap -> page adjustment
            if (background.BackgroundImage != null)
            {
                fx = page.Width.Point / background.BackgroundImage.PixelWidth;
                fy = page.Height.Point / background.BackgroundImage.PixelHeight;
            }

            // Draw each box.
            foreach (var boxDef in pageToRender.DocumentBoxes)
            {
                var result = RenderBox(font, boxDef, fx, fy, gfx, pageToRender, pageIndex, pageTotal);
                if (result.IsFailure)
                {
                    return(result);
                }
            }

            gfx.Restore();

            return(Result.Success());
        }