Questa pagina può contenere testo tradotto automaticamente.

Contenitori composti

I contenitori complessi svolgono un ruolo fondamentale nella strutturazione e nell'organizzazione del contenuto. Usando un contenitore appropriato, puoi presentare facilmente testo e immagini in modo intuitivo.

È certamente possibile costruire un documento usando solo semplici contenitori di testo e immagine. Tuttavia, esistono requisiti difficili o impossibili da implementare usando solo contenitori semplici. I contenitori composti aiutano in questi casi. Inoltre, i contenitori complessi aiutano a raggiungere i tuoi obiettivi con meno codice.

Contenitori composti

Usa i metodi della classe LayoutContainer per aggiungere contenitori complessi come Row e Column alle pagine del documento. Usando questi contenitori, puoi implementare altri contenitori come griglie ed elenchi. Esistono anche i metodi Inlined e Layers per casi meno comuni ma comunque importanti.

Questo articolo fa parte di una serie su Layout API per la generazione di PDF. Se sei nuovo all'API, leggi prima la parte Getting Started with Layout API.

Row

I contenitori Row offrono spazio per elementi disposti orizzontalmente su una sola linea. Ogni elemento in una riga è un contenitore. Ciò significa che puoi inserire contenuti di tipi diversi nella stessa riga. Puoi specificare la spaziatura tra gli elementi usando il metodo Spacing.

Tutti gli elementi in una riga hanno la stessa altezza. La libreria usa l'altezza dell'elemento più alto come altezza della riga. Ci sono tre modi per specificare la larghezza di un elemento. Devi sceglierne uno quando crei l'elemento. Una riga può contenere elementi creati in modi diversi.

Il metodo Row.AutoItem crea un elemento senza una larghezza specificata in modo esplicito. Per tali elementi, la libreria calcola la dimensione intrinseca del loro contenuto. La larghezza del contenuto calcolata è la larghezza dell'elemento. Tieni presente che gli elementi creati con AutoItem non mandano a capo le righe lunghe.

Usa il metodo ConstantItem per creare un elemento con larghezza uguale a un numero esatto di punti.

RelativeItem è utile quando non conosci le larghezze esatte degli elementi nella riga e non vuoi usare le dimensioni intrinseche. In alternativa, puoi specificare larghezze relative per gli elementi nella riga. Il metodo accetta il numero di parti che l'elemento deve occupare. Il numero totale di parti è la somma di tutti i numeri in tutte le chiamate RelativeItem in questa riga.

Ad esempio, se c'è una sola chiamata RelativeItem, il numero non è importante. L'elemento occuperà tutta la larghezza disponibile. Per due o più elementi, i numeri definiscono la proporzione.

row.RelativeItem(2)
row.RelativeItem(3)
row.RelativeItem(1)

Tutti gli elementi creati con il codice sopra occupano 6 parti (2 + 3 + 1 = 6). I singoli elementi occupano rispettivamente 2 di 6, 3 di 6 e 1 di 6 parti.

Layout API usa la seguente formula per calcolare la larghezza di una parte:

PartWidth = (RowWidth - AutoWidth - ConstantWidth) / TotalParts

dove:
RowWidth = larghezza del contenitore riga
AutoWidth = larghezza di tutti gli elementi creati con il metodo AutoItem
ConstantWidth = larghezza di tutti gli elementi creati con il metodo ConstantItem

Ecco un esempio che crea una riga con elementi di tutti e tre i tipi.

var monthNames = DateTimeFormatInfo.InvariantInfo.MonthNames;
var groups = new[]
{
    string.Join(", ", monthNames.Take(4)),
    string.Join(", ", monthNames.Skip(4).Take(4)),
    string.Join(", ", monthNames.Skip(8).Take(4)),
};

PdfDocumentBuilder.Create().Generate("compounds-row.pdf", doc => doc.Pages(page =>
{
    page.Content()
        .Padding(20)
        .MinimalBox()
        .Row(row =>
        {
            row.ConstantItem(100)
                .Background(new PdfRgbColor(187, 237, 237))
                .Text("100 points wide");

            for (int i = 0; i < groups.Length; i++)
            {
                row.AutoItem().LineVertical(0.1);

                var numberOfParts = groups.Length - i + 1;
                row.RelativeItem(numberOfParts)
                    .PaddingHorizontal(5)
                    .Text(t =>
                    {
                        t.Line($"{numberOfParts} parts wide");
                        t.Line();
                        t.Line(groups[i]);
                    });
            }
        });
}));

Puoi vedere il risultato del codice in compounds-row.pdf.

Column

Per disporre gli elementi verticalmente, uno dopo l'altro, usa un contenitore Column. Ogni elemento in una colonna è un contenitore. Per questo motivo, puoi inserire contenuti di tipi diversi in una colonna.

La larghezza di ogni elemento è uguale alla larghezza della colonna. L'altezza di ogni elemento dipende dal contenuto e dalle proprietà dell'elemento. I contenitori Column supportano la suddivisione in pagine, quindi il componente aggiuntivo Layout può rendere elementi di una colonna su più di una pagina.

Per impostazione predefinita, un contenitore Column non ha contenuto di intestazione o piè di pagina. Usa i metodi Header e Footer per accedere e configurare i contenitori corrispondenti. Quando gli elementi della colonna occupano più di una pagina, la libreria ripete sia le intestazioni sia i piè di pagina in каждой pagina.

Usa il metodo Spacing per aggiungere spazio verticale tra gli elementi della colonna. Tieni presente che la libreria non applica spaziatura tra l'intestazione e il primo elemento. La libreria inoltre non aggiunge spazio prima del piè di pagina.

PdfDocumentBuilder.Create().Generate("compounds-column.pdf", doc => doc.Pages(page =>
{
    page.Size(PdfPaperSize.A6);

    page.Content()
        .Padding(20)
        .Column(column =>
        {
            column.Header()
                .Background(new PdfRgbColor(187, 237, 237))
                .Padding(5)
                .Text("Month names");

            for (int i = 0; i < 12; i++)
            {
                column.Item().Background(
                    new PdfGrayColor(i % 2 == 0 ? 90 : 100))
                    .Padding(5)
                    .Text(DateTimeFormatInfo.InvariantInfo.MonthNames[i]);
            }

            column.Footer().LineHorizontal(1);
        });
}));

Puoi vedere il risultato del codice in compounds-column.pdf.

Griglia

I layout a griglia organizzano gli elementi in colonne e righe. Le griglie sono simili alle tabelle sotto questo aspetto. Layout API non fornisce un tipo di contenitore speciale per le griglie. Puoi implementare un layout a griglia usando i contenitori Column e Row.

Aiuta a pensare a una griglia come a una colonna, in cui ciascuno dei suoi elementi è una riga. Sia i contenitori Column sia i contenitori Row offrono la possibilità di impostare la spaziatura tra gli elementi. Se vuoi, puoi avere un'intestazione e un piè di pagina.

Ogni riga può avere un layout indipendente. Può esserci un numero diverso di elementi in ogni riga. Gli elementi possono avere larghezza e altezza diverse. È possibile aggiungere spazio extra prima, dopo o tra gli elementi in una riga. Usa elementi senza contenuto e decorazione per questo.

var blue = new PdfRgbColor(187, 237, 237);
var darkerBlue = blue.Darken(50);
PdfDocumentBuilder.Create().Generate("compounds-grid.pdf", doc => doc.Pages(page =>
{
    page.Size(300, 200);

    page.Content().Padding(15).Column(column =>
    {
        column.Spacing(10);

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.ConstantItem(100).Background(darkerBlue).Height(40);
            row.RelativeItem(4).Background(blue);
        });

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.RelativeItem(2).Background(blue).Height(60);
            row.RelativeItem(1);
            row.RelativeItem(2).Background(blue);
        });

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.RelativeItem(1).Background(blue).Height(50);
            row.RelativeItem(3).Background(blue);
            row.ConstantItem(50).Background(darkerBlue);
        });
    });
}));

Puoi vedere il risultato del codice in compounds-grid.pdf.

Elenchi

Gli elenchi migliorano la leggibilità scomponendo le informazioni in punti concisi. Gli elementi dell'elenco possono avere numeri, punti elenco e altri simboli accanto al testo. Puoi implementare facilmente un layout a elenco usando i contenitori Column e Row. Layout API non fornisce un tipo di contenitore speciale per gli elenchi.

Controlla il codice di esempio che crea un elenco di mesi per stagioni. Tieni presente che l'elenco ha un'intestazione. Se gli elementi contengono testo che può andare a capo nella riga successiva, usa il metodo RelativeItem oppure ConstantItem per la parte di testo dell'elemento.

var monthNames = DateTimeFormatInfo.InvariantInfo.MonthNames.ToList();
monthNames.Insert(0, monthNames[11]);

PdfDocumentBuilder.Create().Generate("compounds-list.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 200);

    page.Content().Padding(5).Column(column =>
    {
        column.Header()
            .Text("Months by seasons:")
            .Style(TextStyle.Parent.Underline());

        for (int i = 0; i < 4; i++)
        {
            var season = string.Join(", ", monthNames.Skip(i * 3).Take(3));
            column.Item().Row(row =>
            {
                row.Spacing(2);

                row.AutoItem().Text("•");
                row.RelativeItem().Text(season);
            });
        }
    });
}));

Puoi vedere il risultato del codice in compounds-list.pdf.

Table

I layout a tabella organizzano gli elementi in colonne e righe. Il tipo di contenitore Table offre un insieme esteso di funzionalità e può aiutarti nei casi più sofisticati. Leggi tutte le funzionalità nell'articolo Contenitore tabella.

InlineContainer

Puoi riempire un contenitore con una raccolta di altri contenitori. Inizia chiamando il metodo LayoutContainer.Inlined. Quindi chiama il metodo Item del InlineContainer fornito per aggiungere contenitori figlio.

Il componente aggiuntivo Layout dispone i contenitori in una riga, uno dopo l'altro. Se non c'è spazio per inserire un elemento, la libreria avvia una nuova riga. Usa i metodi Spacing/HorizontalSpacing/VerticalSpacing per aggiungere spazio tra gli elementi.

Puoi influenzare la posizione degli elementi nel contenitore usando i metodi di allineamento. I metodi AlignTop/AlignMiddle/AlignBottom allineano gli elementi verticalmente. Per la direzione orizzontale, usa i metodi AlignLeft/AlignCenter/AlignRight/AlignJustify.

C'è un caso speciale. Il metodo AlignSpaceAround allinea gli elementi orizzontalmente. Aggiunge anche spaziatura extra prima del primo elemento e dopo l'ultimo elemento.

var orange = new PdfRgbColor(250, 123, 5);
var brown = orange.Darken(50);
var itemProps = new (int Width, PdfColor Color)[] {
    (20, orange), (30, orange), (50, brown), (50, orange), (50, orange),
    (30, orange), (20, brown), (30, orange), (50, brown), (10, brown)
};

PdfDocumentBuilder.Create().Generate("compounds-inlined.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 120);

    page.Content().Inlined(c =>
    {
        c.Spacing(5);

        foreach (var (Width, Color) in itemProps)
            c.Item().Height(30).Width(Width).Background(Color);
    });
}));

Puoi vedere il risultato del codice in compounds-inlined.pdf.

LayerContainer

Potresti dover inserire del contenuto sotto e/o sopra il contenuto principale della pagina. Il caso d'uso ovvio è aggiungere una filigrana sopra le pagine PDF.

Per prima cosa, ottieni un oggetto LayerContainer chiamando il metodo LayoutContainer.Layers. Con questo oggetto, puoi iniziare ad aggiungere livelli. Chiamando il metodo Layer aggiungi un livello secondario. Chiama il metodo PrimaryLayer per aggiungere il livello del contenuto principale. Devi aggiungere esattamente un livello primario.

Layout API compone i livelli nello stesso ordine in cui li crei. I livelli aggiunti prima del livello primario andranno nello sfondo. Tutti i livelli aggiunti dopo il livello primario andranno sopra il contenuto principale. Il contenitore ripete i livelli secondari in tutte le pagine occupate dal contenuto principale.

Ecco un codice di esempio per aggiungere filigrane alle pagine PDF.

PdfDocumentBuilder.Create().Generate("compounds-layers.pdf", doc => doc.Pages(page =>
{
    var largeRedText = TextStyle.Parent.FontSize(48)
        .FontColor(new PdfRgbColor(235, 64, 52));

    page.Size(400, 250);

    page.Content()
        .Padding(25)
        .Layers(layers =>
        {
            layers.Layer()
                .AlignCenter()
                .Text(text => text.CurrentPageNumber().Style(largeRedText));

            layers.PrimaryLayer()
                .Background(new PdfGrayColor(85), 65)
                .Padding(25)
                .Text(new string('_', 790));

            layers.Layer()
                .AlignCenter()
                .AlignMiddle()
                .Text("Watermark")
                .Style(largeRedText);
        });
}));

Puoi vedere il risultato del codice in compounds-layers.pdf.

Filigrane

Uno dei requisiti più comuni è aggiungere una filigrana al PDF. Il requisito può esistere per una varietà di motivi. Puoi aggiungere una filigrana al PDF per identificare la proprietà o per evidenziare la riservatezza o la sensibilità delle informazioni nel PDF.

Un approccio alla filigranatura dei PDF consiste nell'usare i livelli. Vedi l'esempio nella sezione precedente. Ti mostro un altro approccio.

Userò i contenitori di sfondo e primo piano della pagina per applicare filigrane ai PDF. Il componente aggiuntivo Layout ripete questi contenitori nelle pagine successive. Ogni pagina con contenuto principale del documento conterrà anche contenitori di sfondo e primo piano. Questo li rende adatti al compito.

Inizio aggiungendo un'immagine al background container. Puoi aggiungere il tuo logo al PDF nello stesso modo. L'immagine apparirà dietro il contenuto della pagina. Non importa quante pagine del documento usino l'immagine di sfondo. L'API aggiungerà una sola copia dei byte dell'immagine al PDF generato.

Il contenuto principale del documento può essere qualsiasi cosa. Per questo codice di esempio, uso il famoso testo Lorem Ipsum.

La filigrana testuale va nel foreground container. Il testo stesso può essere qualsiasi cosa e puoi usare qualsiasi colore o font. Ho usato testo ruotato disegnato con lettere rosse semitrasparenti di dimensione maggiore.

Il codice di esempio scarica in modo asincrono sia l'immagine sia il testo dal nostro repository di codice di esempio. Naturalmente, puoi usare un'immagine locale e/o leggere il testo da un file.

var urlPrefix =
    "https://raw.githubusercontent.com/BitMiracle/Docotic.Pdf.Samples/master/Samples/Sample%20Data/";

using var client = new HttpClient();
using var imageResponse = await client.GetAsync(urlPrefix + "watermark-background.png");
using var imageStream = await imageResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

var loremIpsum = string.Empty;
using (var textResponse = await client.GetAsync(urlPrefix + "lorem-ipsum.txt"))
     loremIpsum = await textResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

PdfDocumentBuilder.Create().Generate("compounds-watermarks.pdf", doc =>
{
    var image = doc.Image(imageStream);

    doc.Pages(page =>
    {
        page.Size(400, 250);

        page.Background()
            .AlignMiddle()
            .Image(image);

        page.Content()
            .Padding(25)
            .Text(loremIpsum);

        var largeRedText = TextStyle.Parent.FontSize(48)
            .FontColor(new PdfRgbColor(235, 64, 52), 50);
        page.Foreground()
            .Rotate(-30)
            .Translate(-50, 180)
            .Text("DO NOT COPY")
            .Style(largeRedText);
    });
});

Puoi vedere il risultato del codice in compounds-watermarks.pdf.

Codice d'esempio

Abbiamo alcune app di esempio che trattano le funzionalità menzionate in maggiore dettaglio. Dedica un po' di tempo a esaminarle.