Exemplo n.º 1
0
        public Page nextPage()
        {
            // walk all pages found in .mcf, return null on last
            if (_pageIterator >= _pages.Count)
            {
                return(null);                               // handle out of bounds
            }
            // the current xml page
            XmlNode xmlPage = _pages[_pageIterator];

            // the reconstructed page (still empty)
            Page page = new Page();

            // keep track which page we are currently processing, every Page object actually consists of
            // two <fotobook/page> nodes, the left and right side in photobook
            bool isDouble = false;

            // need to collect double pages here, in case of cover actually 3 pages.
            while (xmlPage != null)
            {
                // the current page type, later used to handle left/right and special page cases
                page.type = Page.convert(xmlPage.Attributes.GetNamedItem("type").Value);

                // store page number
                if (page.type == Page.Type.Normalpage)
                {
                    if (isDouble)
                    {
                        page.pageNoRight = getAttributeStr(xmlPage, "pagenr");
                    }
                    else
                    {
                        page.pageNoLeft = getAttributeStr(xmlPage, "pagenr");
                    }
                }

                // iterate all sub nodes this page contains
                foreach (XmlNode node in xmlPage.ChildNodes)
                {
                    switch (node.Name)
                    {
                    // bundlesize is the left & right combined size of the page
                    case "bundlesize":
                        page.bundleSize = new Vector2(getAttributeF(node, "width"), getAttributeF(node, "height"));
                        break;

                    // NOTE: currently only handles background id
                    case "designElementIDs":
                        // store background for left and right individually
                        if (!isDouble)
                        {
                            page.backgroundLeft = getAttributeStr(node, "background");
                        }
                        else
                        {
                            page.backgroundRight = getAttributeStr(node, "background");
                        }
                        break;

                    // area is the root class of all content objects
                    // NOTE: currently supports <imagearea> & <textarea>
                    case "area":
                        // get the type of current area
                        string type = node.Attributes.GetNamedItem("areatype").Value;

                        // trick area system...
                        if (type == "spinetextarea")
                        {
                            type           = "textarea";
                            page.spineSize = getAttributeF(node.SelectSingleNode("position"), "height");
                        }

                        Area newArea;

                        switch (type)
                        {
                        case "imagearea": {
                            // imagearea? image subnode exists!
                            XmlNode image = node.SelectSingleNode("image");

                            // the image file name stored in .mcf file (in format: "safecontainer:/imageName.jpg)
                            string filename = getAttributeStr(image, "filename");

                            // replace 'safecontainer:/' with actual path, in case filename does not exist,
                            // store "NULL", will render as magenta outline and print error.
                            string filePath = filename != "" ? filename.Replace("safecontainer:/", _safeContainerPath) : "NULL";

                            // get & store cutout information
                            XmlNode cutout        = image.SelectSingleNode("cutout");
                            Vector2 cutoutLeftTop = new Vector2(getAttributeF(cutout, "left"), getAttributeF(cutout, "top"));
                            float   scale         = getAttributeF(cutout, "scale", 1.0f);

                            // construct new area
                            newArea = new ImageArea()
                            {
                                path   = filePath,
                                cutout = cutoutLeftTop,
                                scale  = scale,
                            };

                            // get & store border settings
                            XmlNode border = node.SelectSingleNode("decoration/border");
                            if (border != null)
                            {
                                newArea.border      = true;
                                newArea.borderWidth = getAttributeF(border, "width");
                                newArea.borderColor = getAttributeStr(border, "color");
                            }

                            break;
                        }

                        case "imagebackgroundarea": {
                            // handle backgroundimages literally just like normal images.
                            // TODO: de-duplicate this code as much as possible

                            XmlNode imgbg = node.SelectSingleNode("imagebackground");

                            // the image file
                            string filename = getAttributeStr(imgbg, "filename");

                            // replace 'safecontainer:/' with actual path, in case filename does not exist,
                            // store "NULL", will render as magenta outline and print error.
                            string filePath = filename != "" ? filename.Replace("safecontainer:/", _safeContainerPath) : "NULL";

                            // get & store cutout information
                            XmlNode cutout        = imgbg.SelectSingleNode("cutout");
                            Vector2 cutoutLeftTop = new Vector2(getAttributeF(cutout, "left"), getAttributeF(cutout, "top"));
                            float   scale         = getAttributeF(cutout, "scale", 1.0f);

                            string bgPosition = getAttributeStr(imgbg, "backgroundPosition");
                            ImageBackgroundArea.ImageBackgroundType bgtype = ImageBackgroundArea.ImageBackgroundType.Undefined;

                            if (bgPosition == "LEFT_OR_TOP")
                            {
                                bgtype = ImageBackgroundArea.ImageBackgroundType.Left;
                            }
                            else if (bgPosition == "RIGHT_OR_BOTTOM")
                            {
                                bgtype = ImageBackgroundArea.ImageBackgroundType.Right;
                            }
                            else if (bgPosition == "BUNDLE")
                            {
                                bgtype = ImageBackgroundArea.ImageBackgroundType.Bundle;
                            }
                            else
                            {
                                Log.Error("Unhandled background image position: " + bgPosition);
                            }

                            // construct new area
                            newArea = new ImageBackgroundArea()
                            {
                                path   = filePath,
                                cutout = cutoutLeftTop,
                                scale  = scale,
                                type   = bgtype
                            };

                            break;
                        }

                        case "textarea": {
                            // in <textarea> these exist:
                            XmlNode text       = node.SelectSingleNode("text");
                            XmlNode textFormat = text.SelectSingleNode("textFormat");

                            // NOTE: <font> stores several comma-separated values: Fontname,Fonstsize,...and more. Currently only handles these two
                            string[] fontInfo = getAttributeStr(textFormat, "font").Split(",");

                            // get the fontsize, take pdf scale into account and adjust to photobook settings
                            int fontSize = (int)(Convert.ToInt32(fontInfo[1]) * SCALE * FONT);             // somewhat matches the result in photobook

                            // text color
                            string color = getAttributeStr(textFormat, "foregroundColor");

                            // text box background color
                            string bgColor = getAttributeStr(textFormat, "backgroundColor");

                            // by default align left top
                            string alignLabel  = "ALIGNLEFT";
                            string valignLabel = "ALIGNVTOP";

                            // NOTE: <align> sometimes holds two comma-separated values (Horizontal and Vertical alignment)
                            // for now only handles second (horizontal).
                            string[] align = getAttributeStr(textFormat, "Alignment").Split(",");
                            alignLabel = align.Last();
                            if (align.Length > 1)
                            {
                                valignLabel = align.First();
                            }

                            string str = extractTextFromHTML(text.InnerText, ref color);

                            // construct new area
                            newArea = new TextArea()
                            {
                                textElements    = extractTextFromHTMLv2(text.InnerText),
                                text            = str,
                                fontsize        = fontSize,
                                color           = color,
                                font            = fontInfo[0],
                                align           = alignLabel,
                                valign          = valignLabel,
                                backgroundcolor = bgColor,
                            };

                            break;
                        }

                        default:
                            // there are more areatypes, for now just create an empty area that wont draw anything
                            // and inform user.
                            newArea = new Area();
                            Log.Warning("Unhandled area type in <page/area> '" + type + "'.");
                            break;
                        }

                        // sanity check, cant be null really :P
                        if (newArea == null)
                        {
                            break;
                        }

                        // all areas contain position information
                        XmlNode position = node.SelectSingleNode("position");

                        // apply position information to current area
                        newArea.rect = new RectangleF()
                        {
                            X      = getAttributeF(position, "left"),
                            Y      = getAttributeF(position, "top"),
                            Width  = getAttributeF(position, "width"),
                            Height = getAttributeF(position, "height")
                        };
                        newArea.rotation = getAttributeF(position, "rotation") / SCALE;     // undo scale for rotation

                        // store new page in list
                        page.areas.Add(newArea);
                        break;

                    default:
                        // inform user about unhandled node
                        Log.Warning("Unhandled Node in <page> '" + node.Name + "'.");
                        break;
                    }
                }

                // Handle all these specific page types and cases

                if (page.type == Page.Type.Fullcover)
                {
                    // check if next page is spine, otherwise this was the back side of the cover
                    XmlNode   nextPage = _pages[_pageIterator + 1];
                    Page.Type nextType = Page.convert(nextPage.Attributes.GetNamedItem("type").Value);
                    if (nextType == Page.Type.Spine)
                    {
                        xmlPage = nextPage;
                    }
                    else
                    {
                        xmlPage = null;  // cover page is done, proceed
                    }
                }
                else if (page.type == Page.Type.Spine)
                {
                    // check if next page is Fullcover (should be anyway)
                    XmlNode   nextPage = _pages[_pageIterator + 1];
                    Page.Type nextType = Page.convert(nextPage.Attributes.GetNamedItem("type").Value);
                    if (nextType == Page.Type.Fullcover)
                    {
                        xmlPage = nextPage;
                    }
                    else
                    {
                        xmlPage = null;
                    }
                }
                else if (page.type == Page.Type.Emptypage)
                {
                    // check if next page exists... otherwise end of book.
                    if (_pageIterator + 1 < _pages.Count)
                    {
                        XmlNode nextPage = _pages[_pageIterator + 1];
                        xmlPage  = nextPage;
                        isDouble = true;
                    }
                    else
                    {
                        xmlPage = null;
                    }
                }
                else if (page.type == Page.Type.Normalpage && !isDouble)
                {
                    XmlNode nextPage = _pages[_pageIterator + 1];
                    // next is second half of a double page...
                    xmlPage  = nextPage;
                    isDouble = true;
                }
                else
                {
                    // this was second half of a double page, continue with new page
                    xmlPage = null;
                }

                // increment to handle next <page> object in list
                _pageIterator++;
            }

            // return the newly constructed page
            return(page);
        }
Exemplo n.º 2
0
        public void writePage(Page pPage)
        {
            // page size is given per <fotobook/page>. iTextSharp needs it set before adding page or opening document.
            _doc.SetPageSize(new Rectangle(0f, 0f, pPage.bundleSize.X, pPage.bundleSize.Y));

            // handle first page case
            try {
                if (!_doc.IsOpen())
                {
                    _doc.Open();
                }
                else
                {
                    _doc.NewPage();
                }
            } catch (Exception e) {
                Log.Error("Creating pdf page failed with error: '" + e.Message + "'.");
                return;
            }


            PdfContentByte canvas = _writer.DirectContent;

            // TOOD: de-duplicate
            // draw left part of background
            if (pPage.backgroundLeft != null)
            {
                canvas.Rectangle(0, 0, pPage.bundleSize.X / 2, pPage.bundleSize.Y);
#if DEBUG || _DEBUG
                canvas.SetColorFill(BaseColor.CYAN);
#else
                canvas.SetColorFill(BaseColor.WHITE);
#endif
                canvas.Fill();

                string id = pPage.backgroundLeft;
                System.Drawing.Image sysImg = DesignIdConverter.getImageFromID(id);
                if (sysImg == null)
                {
                    Log.Error("Background image for id '" + id + "' was null.");
#if DEBUG || _DEBUG
                    canvas.SetColorFill(BaseColor.MAGENTA);
#else
                    canvas.SetColorFill(BaseColor.WHITE);
#endif
                    canvas.Fill();
                }
                else
                {
                    Image img = sysImageToITextImage(sysImg);

                    float facY = pPage.bundleSize.Y / img.PlainHeight;
                    float facX = pPage.bundleSize.X / img.PlainWidth;
                    float fac  = Math.Max(facX, facY);

                    img.ScalePercent(fac * 100f);

                    float yoffset = (img.ScaledHeight - pPage.bundleSize.Y) * -0.5f;
                    float xoffset = (img.ScaledWidth - pPage.bundleSize.X) * -0.5f;

                    img.SetAbsolutePosition(xoffset, yoffset);

                    float width = -xoffset + ((pPage.type == Page.Type.Fullcover) ? pPage.bundleSize.X : pPage.bundleSize.X / 2f);

                    Image imgCropped = cropImage(img, _writer, 0, 0, width, img.ScaledHeight);

                    imgCropped.SetAbsolutePosition(xoffset, yoffset);
                    canvas.AddImage(imgCropped);
                }
            }

            // draw right background
            if (pPage.backgroundRight != null)
            {
                canvas.Rectangle(pPage.bundleSize.X / 2, 0, pPage.bundleSize.X / 2, pPage.bundleSize.Y);
#if DEBUG || _DEBUG
                canvas.SetColorFill(BaseColor.CYAN);
                canvas.SetColorFill(BaseColor.WHITE);
#endif
                canvas.Fill();

                string id = pPage.backgroundRight;
                System.Drawing.Image sysImg = DesignIdConverter.getImageFromID(id);
                if (sysImg == null)
                {
                    Log.Error("Background image for id '" + id + "' was null.");
#if DEBUG || _DEBUG
                    canvas.SetColorFill(BaseColor.MAGENTA);
#else
                    canvas.SetColorFill(BaseColor.WHITE);
#endif
                    canvas.Fill();
                    canvas.Fill();
                }
                else
                {
                    Image img = sysImageToITextImage(sysImg);

                    float facY = pPage.bundleSize.Y / img.PlainHeight;
                    float facX = pPage.bundleSize.X / img.PlainWidth;
                    float fac  = Math.Max(facX, facY);

                    img.ScalePercent(fac * 100f);

                    float yoffset = (img.ScaledHeight - pPage.bundleSize.Y) * -0.5f;
                    float xoffset = (img.ScaledWidth - pPage.bundleSize.X) * -0.5f;

                    img.SetAbsolutePosition(xoffset, yoffset);

                    Image imgCropped = cropImage(img, _writer, pPage.bundleSize.X / 2f, 0, img.ScaledWidth, img.ScaledHeight);

                    imgCropped.SetAbsolutePosition(xoffset + pPage.bundleSize.X / 2, yoffset);
                    canvas.AddImage(imgCropped);
                }
            }

            // draw all supported content areas stored in this page
            foreach (Area area in pPage.areas)
            {
                // calculate rect dimensions // TODO: de-duplicate?
                float pX = area.rect.X;
                float pY = pPage.bundleSize.Y - area.rect.Y - area.rect.Height;

                // handle rotation
                canvas.SaveState();
                AffineTransform tf    = new AffineTransform();
                double          angle = area.rotation * Math.PI / 180.0;
                tf.Rotate(-angle, pX + area.rect.Width / 2f, pY + area.rect.Height / 2f); // rotate around center ccw
                canvas.Transform(tf);

                if (area is ImageArea || area is ImageBackgroundArea)
                {
                    // TODO: This is somewhat hacky - there is probably a better way to do this.
                    if (area is ImageBackgroundArea)
                    {
                        ImageBackgroundArea bgArea = (ImageBackgroundArea)area;

                        if (bgArea.type == ImageBackgroundArea.ImageBackgroundType.Right)
                        {
                            bgArea.rect.X += pPage.bundleSize.X / 2f + pPage.spineSize / 2f;
                        }
                    }

                    ImageArea imgArea = (ImageArea)area;

                    // if image path was not valid draw magenta outline and print error
                    if (imgArea.path == "NULL")
                    {
#if DEBUG || _DEBUG
                        // calculate rect dimensions
                        Rectangle nullRect = new Rectangle(pX, pY, pX + imgArea.rect.Width, pY + imgArea.rect.Height);

                        // configure border
                        nullRect.Border      = 1 | 2 | 4 | 8;
                        nullRect.BorderColor = BaseColor.MAGENTA;
                        nullRect.BorderWidth = 4.0f;

                        // draw to document
                        canvas.Rectangle(nullRect);

                        Log.Error("Image path was null. Probably caused by an empty image area.");
                        canvas.RestoreState();
#endif
                        continue;
                    }

                    // load image file.
                    System.Drawing.Image sysImg;
                    try {
                        sysImg = System.Drawing.Image.FromFile(imgArea.path);
                    } catch (System.Exception e) {
                        if (e is System.IO.FileNotFoundException)
                        {
                            Log.Error("Loading image failed. Image at '" + imgArea.path + "' not found.");
                        }
                        else
                        {
                            Log.Error("Loading image failed wit error: '" + e.Message + "'");
                        }
                        canvas.RestoreState();
                        continue;
                    }


                    // fix exif orientation
                    ExifRotate(sysImg);

                    // calculate resizing factor, results in equal pixel density for all images.
                    float scale = 1f / imgArea.scale * Config.ImgScale; // the higher this value, the lower pixel density is. 0.0f = original resolution
                    scale = scale < 1.0f ? 1.0f : scale;                // never scale image up

                    System.Drawing.Size newSize = new System.Drawing.Size((int)(sysImg.Width / scale), (int)(sysImg.Height / scale));

                    // resize image
                    sysImg = (System.Drawing.Image)(new System.Drawing.Bitmap(sysImg, newSize));

                    Image img = sysImageToITextImage(sysImg);

                    // apply scale as defined in .mcf
                    img.ScalePercent(imgArea.scale * 100.0f * scale);

                    // calculate image position in pdf page
                    float posX = imgArea.rect.X + imgArea.cutout.X;
                    float posY = pPage.bundleSize.Y - imgArea.rect.Y - imgArea.rect.Height; // pdf origin is in lower left, mcf origin is in upper left

                    // yaaaaa... whatever. This way everything fits
                    float cropBottom = img.ScaledHeight - imgArea.rect.Height + imgArea.cutout.Y;

                    // crop image to mcf specified rect
                    Image cropped = cropImage(img, _writer, -imgArea.cutout.X, cropBottom, imgArea.rect.Width, imgArea.rect.Height);

                    // move to mcf specified position
                    cropped.SetAbsolutePosition(imgArea.rect.X, posY);

                    string imgType = area is ImageBackgroundArea ? "ImageBackground" : "Image";
                    Log.Info("Rendering " + imgType + " (." + imgArea.path.Split(".").Last() + "): " +
                             "original: " + sysImg.Width + "x" + sysImg.Height + "; " +
                             "scaled: " + newSize.Width + "x" + newSize.Height + "; " +
                             "cropped: " + (int)cropped.Width + "x" + (int)cropped.Height + "; " +
                             "at: " + (int)cropped.AbsoluteX + ", " + (int)cropped.AbsoluteY);

                    // draw the image
                    canvas.AddImage(cropped);

                    // draw image border if specified in .mcf
                    if (imgArea.border)
                    {
                        // TODO mcf as an outside property that is currently not taken into account.
                        // seems like all borders are 'outside' in photobook.
                        // iTextSharp draws Borders centered (BorderWidth/2 pixels overlap image)
                        // this should be corrected.

                        // calc border rect
                        Rectangle rect = new Rectangle(pX, pY, pX + imgArea.rect.Width, pY + imgArea.rect.Height);

                        // convert .mcf's html style color hex code to Color, based on: https://stackoverflow.com/a/2109904
                        int argb = Int32.Parse(imgArea.borderColor.Replace("#", ""), System.Globalization.NumberStyles.HexNumber);
                        System.Drawing.Color clr = System.Drawing.Color.FromArgb(argb);

                        // configure border
                        rect.Border      = 1 | 2 | 4 | 8;
                        rect.BorderColor = new BaseColor(clr);
                        rect.BorderWidth = imgArea.borderWidth;

                        // draw border
                        canvas.Rectangle(rect);
                    }
                }
                else if (area is TextArea)
                {
                    TextArea textArea = (TextArea)area;

                    // Render text background if not transparent
                    if (!textArea.backgroundcolor.EndsWith("00"))
                    {
                        Log.Info("Rendering Text background: color=" + textArea.backgroundcolor);

                        canvas.Rectangle(pX, pY, textArea.rect.Width, textArea.rect.Height);
                        canvas.SetColorFill(argb2BaseColor(textArea.backgroundcolor));
                        canvas.Fill();
                    }

                    // just in case something went wrong
                    if (String.IsNullOrWhiteSpace(textArea.text))
                    {
                        Log.Error("Text was empty.");
                        canvas.RestoreState();
                        continue;
                    }
                    else
                    {
                        Log.Info("Rendering Text: font=" + textArea.font + "; size=" + textArea.fontsize + "; align=" + textArea.align + "; valign=" + textArea.valign);
                    }

                    // iTextSharp textbox
                    ColumnText colText = new ColumnText(canvas);


                    // calculate rect
                    float     llx      = textArea.rect.X;
                    float     lly      = pPage.bundleSize.Y - textArea.rect.Y - textArea.rect.Height;
                    float     urx      = llx + textArea.rect.Width;
                    float     ury      = lly + textArea.rect.Height;
                    Rectangle textRect = new Rectangle(llx, lly, urx, ury);

                    // apply rect to textbox
                    colText.SetSimpleColumn(textRect);

                    // The actual text object
                    Paragraph par = new Paragraph();

                    // magic number that closely matches photobook
                    // TODO there is probably more information in the .mcf's css part
                    par.SetLeading(0, 1.3f);

                    // apply corrent alignment
                    if (textArea.align == "ALIGNHCENTER")
                    {
                        par.Alignment = Element.ALIGN_CENTER;
                    }
                    else if (textArea.align == "ALIGNLEFT")
                    {
                        par.Alignment = Element.ALIGN_LEFT;
                    }
                    else if (textArea.align == "ALIGNRIGHT")
                    {
                        par.Alignment = Element.ALIGN_RIGHT;
                    }
                    else if (textArea.align == "ALIGNJUSTIFY")
                    {
                        par.Alignment = Element.ALIGN_JUSTIFIED;
                    }
                    else
                    {
                        Log.Warning("Unhandled text align: '" + textArea.align + "'.");
                    }

                    // add text chunks
                    foreach (TextElement elem in textArea.textElements)
                    {
                        int style = 0;
                        style += elem.bold ? Font.BOLD : 0;
                        style += elem.italic ? Font.ITALIC : 0;
                        style += elem.underlined ? Font.UNDERLINE : 0;
                        Font fnt = FontFactory.GetFont(elem.family, elem.size, style, argb2BaseColor(elem.color));

                        par.Add(new Chunk(elem.text + (elem.newline ? "\n" : " "), fnt));
                    }

                    int valign = 0;
                    if (textArea.valign == "ALIGNVCENTER")
                    {
                        valign = Element.ALIGN_MIDDLE;
                    }
                    else if (textArea.valign == "ALIGNVTOP")
                    {
                        valign = Element.ALIGN_TOP;
                    }
                    else if (textArea.valign == "ALIGNVBOTTOM")
                    {
                        valign = Element.ALIGN_BOTTOM;
                    }
                    else
                    {
                        Log.Warning("Unhandled text vertical align: '" + textArea.valign + "'.");
                    }

                    // v align needs a table...
                    PdfPTable table = new PdfPTable(1);
                    table.SetWidths(new int[] { 1 });
                    table.WidthPercentage = 100;
                    table.AddCell(new PdfPCell(par)
                    {
                        HorizontalAlignment = par.Alignment,
                        VerticalAlignment   = valign,
                        FixedHeight         = textArea.rect.Height,
                        Border = 0,
                    });

                    // add paragraph to textbox
                    colText.AddElement(table);

                    // draw textbox
                    colText.Go();
                }

                // restore canvas transform before rotation
                canvas.RestoreState();
            }

            // draw pagenumbers
            // TODO remove magic numbers, at least comment
            const float PAGE_NR_Y_OFFSET  = -4.0f;
            const float PAGE_NR_X_OFFSET  = 0.0f;
            float       PAGE_NR_FONT_SIZE = Page.pageNoFontSize * 1.1f;
            float       PAGE_NR_HEIGHT    = PAGE_NR_FONT_SIZE + 12.0f; // add some extra space... this is needed.
            float       PAGE_Y_POS        = Page.pageNoMargin.Y + PAGE_NR_Y_OFFSET;

            // TODO de-duplicate all these conversions and move to helper method
            // convert .mcf's html style color hex code to Color, based on: https://stackoverflow.com/a/2109904
            int argb_ = Int32.Parse(Page.pageNoColor.Replace("#", ""), System.Globalization.NumberStyles.HexNumber);
            System.Drawing.Color clr_ = System.Drawing.Color.FromArgb(argb_);

            // left
            Paragraph pageNoLeft = new Paragraph(pPage.pageNoLeft, FontFactory.GetFont(Page.pageNoFont, PAGE_NR_FONT_SIZE, new BaseColor(clr_)));
            pageNoLeft.Alignment = Element.ALIGN_LEFT + Element.ALIGN_BOTTOM;

            ColumnText leftNo     = new ColumnText(_writer.DirectContent);
            Rectangle  leftNoRect = new Rectangle(Page.pageNoMargin.X + PAGE_NR_X_OFFSET, PAGE_Y_POS, 500, PAGE_Y_POS + PAGE_NR_HEIGHT);
            leftNo.SetSimpleColumn(leftNoRect);

            leftNo.AddElement(pageNoLeft);
            leftNo.Go();

            //leftNoRect.Border = 1 | 2 | 4 | 8;
            //leftNoRect.BorderColor = BaseColor.GREEN;
            //leftNoRect.BorderWidth = 1.0f;
            //_writer.DirectContent.Rectangle(leftNoRect);

            // right
            Paragraph pageNoRight = new Paragraph(pPage.pageNoRight, FontFactory.GetFont(Page.pageNoFont, PAGE_NR_FONT_SIZE, new BaseColor(clr_)));
            pageNoRight.Alignment = Element.ALIGN_RIGHT;

            ColumnText rightNo     = new ColumnText(_writer.DirectContent);
            Rectangle  rightNoRect = new Rectangle(pPage.bundleSize.X - Page.pageNoMargin.X - PAGE_NR_X_OFFSET - 500, PAGE_Y_POS, pPage.bundleSize.X - Page.pageNoMargin.X - PAGE_NR_X_OFFSET, PAGE_Y_POS + PAGE_NR_HEIGHT);
            rightNo.SetSimpleColumn(rightNoRect);

            rightNo.AddElement(pageNoRight);
            rightNo.Go();

            //rightNoRect.Border = 1 | 2 | 4 | 8;
            //rightNoRect.BorderColor = BaseColor.YELLOW;
            //rightNoRect.BorderWidth = 1.0f;
            //_writer.DirectContent.Rectangle(rightNoRect);

            //Console.WriteLine("Page drawn: " + pPage.type.ToString() + " left: " + pPage.pageNoLeft + "; right: " + pPage.pageNoRight + "!");
        }