Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What value to use for .MoveUp of canvas

Tags:

c#

.net

pdf

itext7

The code below copies all pages from a PDF file to a new file and inserts on the first page a rectangle at the top with a red border holding a short text.

If I don't move it, a gap will be left at the top (here enlarged a lot, font size is 8 only):

enter image description here

However, if I move the rectangle up by an empiric value of 4:

    iText.Kernel.Geom.Rectangle pageSize = firstPage.GetCropBox().MoveUp(4);

there will be a perfect match at the top:

enter image description here

The value 4 is not related to the font size.

I dislike magic numbers in code so, my question is: Why 4? What expression would reveal this value of 4?

The code line is in the first method here. The second is where it is used, and third is called from the first; it just supplies a style:

private static void RegisterDocument(PdfDocument pdfDocument, string registration)
{
    // Magic value to close gap between top of page and top of rectangle with the registration.
    const float moveUp = 4F;

    Document document = new Document(pdfDocument, new PageSize(PageSize.A4));
    PdfPage firstPage = document.GetPdfDocument().GetFirstPage();

    Paragraph paragraph = new Paragraph(registration).AddStyle(RegistrationStyle());

    iText.Kernel.Geom.Rectangle pageSize = firstPage.GetCropBox().MoveUp(moveUp);
    LayoutContext layoutContext = new LayoutContext(new LayoutArea(1, pageSize));
    IRenderer renderer = paragraph.CreateRendererSubTree();
    renderer.SetParent(document.GetRenderer()).Layout(layoutContext);

    Canvas canvas = new Canvas(new PdfCanvas(firstPage, true), pageSize);
    canvas.Add(paragraph);          

    document.Close();   
}

public static void RegisterPdf(string sourceFilename, string targetFilename, string registration)
{
    if (registration.Length > 0)
    {
        // Open source and target PDF files.
        PdfDocument sourcePdf = new PdfDocument(new PdfReader(sourceFilename));
        PdfDocument targetPdf = new PdfDocument(new PdfWriter(targetFilename));

        // Copy all pages from source PDF to target PDF.
        sourcePdf.CopyPagesTo(1, sourcePdf.GetNumberOfPages(), targetPdf);

        // Add registration to page 1 of target and save the document.
        RegisterDocument(targetPdf, registration);

        // Close the files.
        sourcePdf.Close();
        targetPdf.Close();
    }
}

private static Style RegistrationStyle()
{
    // Fixed design values for font and rectangle.
    PdfFont font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA);
    const float fontSize = 8F;
    const float rightPadding = 3F;
    TextAlignment textAlignment = TextAlignment.RIGHT;
    iText.Kernel.Colors.Color borderColor = ColorConstants.RED;
    iText.Kernel.Colors.Color fillColor = ColorConstants.WHITE;
    const float borderWidth = 0.7F;

    Style style = new Style()
        .SetFont(font)
        .SetFontSize(fontSize)
        .SetPaddingRight(rightPadding)
        .SetTextAlignment(textAlignment)
        .SetBackgroundColor(fillColor)
        .SetBorder(new SolidBorder(borderColor, borderWidth));

    return style;
}
like image 984
Gustav Avatar asked Nov 23 '20 16:11

Gustav


2 Answers

You wonder

I dislike magic numbers in code so, my question is: Why 4? What expression would reveal this value of 4?

iText, when calculating the layout of some entity, retrieves properties from multiple sources, in particular the entity itself and its renderer. And it does not only ask them for explicitly set properties but also for defaults.

In the case at hand you see the default top margin value of the Paragraph class at work:

public override T1 GetDefaultProperty<T1>(int property) {
    switch (property) {
        case Property.LEADING: {
            return (T1)(Object)new Leading(Leading.MULTIPLIED, childElements.Count == 1 && childElements[0] is Image ? 
                1 : 1.35f);
        }

        case Property.FIRST_LINE_INDENT: {
            return (T1)(Object)0f;
        }

        case Property.MARGIN_TOP:
        case Property.MARGIN_BOTTOM: {
            return (T1)(Object)UnitValue.CreatePointValue(4f);
        }

        case Property.TAB_DEFAULT: {
            return (T1)(Object)50f;
        }

        default: {
            return base.GetDefaultProperty<T1>(property);
        }
    }
}

(iText Layout Paragraph method)

If you set the top margin of your paragraph to 0, you can simplify your code considerably:

public static void RegisterPdfImproved(string sourceFilename, string targetFilename, string registration)
{
    using (PdfDocument pdf = new PdfDocument(new PdfReader(sourceFilename), new PdfWriter(targetFilename)))
    using (Document document = new Document(pdf))
    {
        document.SetMargins(0, 0, 0, 0);
        Paragraph paragraph = new Paragraph(registration)
            .AddStyle(RegistrationStyle())
            .SetMarginTop(0);
        document.Add(paragraph);
    }
}

Without any magic values you now get

screen shot

like image 128
mkl Avatar answered Nov 17 '22 06:11

mkl


There is no way to tell, without seeing all the details of your code. It could depend on an arbitrary number of circumstances and combinations of them. Examples:

  • default values in the PDF library you are using
  • margins defined in the document
like image 43
EricSchaefer Avatar answered Nov 17 '22 06:11

EricSchaefer