該頁面可以包含自動翻譯的文字。
容器的大小、位置和渲染
內容是文件最重要的部分。 這毋庸置疑。 另一個關鍵部分是創建清晰、專業和有效溝通的格式。 格式正確的文 件具有視覺吸引力、可讀性且易於瀏覽。
您可能已經知道如何使用容器組織內容。 以及如何為它們應用背景顏色。 本文介紹如何指定容器的大小和位 置。 它還涵蓋了內容的條件渲染等高級功能。 有有關支持從右到左內容方向的資訊。
LayoutContainer 類別提供了專業地安排容器所需的一 切。 透過套用填滿和對齊,您可以建立給使用者留下正面印象的文件。
本文是用於 PDF 產生的 Layout API 的系列文章的一部分。 如果您是 API 新手,請先閱讀 Layout API 入 門 部分。
9.5.17615-dev 9.5.17615-dev14,813 已通過 NuGet 總下載量 4,924,084
尺寸
預設情況下,容器佔據其內容所需的最小區域。 換句話說,容器的大小等於其內容的固有大小。
影像的固有大小由影像檔案本身的尺寸決定。 文字範圍的固有大小是覆蓋該範圍中所有字形的區域的大小。
像 Column 或 Table 這樣的複合容器的大小取決於容器部件的大小。
Width & Height
可以使用 Width 和 Height 方法指定容器的精確寬度和高度。 這對 於佔位符容器來說非常方便。
精確的尺寸也適用於影像。 這是因為 Layout API 根據它們的 ImageContentMode 縮放它們以適合或填充容器。
您應該注意複合容器和帶有文字的容器的確切尺寸。 當無法適應提供的尺寸的內容時,您將收到 LayoutException。
在某些情況下,您只想設定寬度或高度的限制。 您可以使用 MinWidth、 MinHeight、 MaxWidth 和 MaxHeight 方法設定約束。
請注意,當無法滿足約束時,程式庫將拋出 LayoutException
。
Extend
容器可以自行擴展以佔據最大的可用空間。 當您不知道確切的尺寸或尺寸限制時,這會派上用場。
當您希望容器僅佔用水平方向上的所有可用空間時,請使用 ExtendHorizontal 方法。 當您只想 在垂直方向上擴展容器 時,ExtendVertical 非常有用。 Extend 方法使容器佔用兩個方向上的所有可用 空間。
var gray = new PdfGrayColor(75);
var text = "Content goes here";
var size = new PdfSize(150, 50);
PdfDocumentBuilder.Create().Generate("positioning-extend.pdf", doc =>
{
for (int i = 0; i < 4; i++)
{
doc.Pages(page =>
{
page.Size(size);
page.Content().Row(r =>
{
var container = r.AutoItem().Background(gray);
switch (i)
{
case 0:
container.Text(text);
break;
case 1:
container.ExtendHorizontal().Text(text);
break;
case 2:
container.ExtendVertical().Text(text);
break;
case 3:
container.Extend().Text(text);
break;
}
});
});
}
});
上述程式碼的結果在 positioning-extend.pdf。 正 如您所看到的,四個頁面中的每一頁都在灰色背景上包含相同的文字。 但每個頁面上容器的大小是不同的。
MinimalBox
LayoutContainer 提供了
MinimalBox 方法。 它與
Extend 方法相反。 MinimalBox
產生只使用
內容所需最小空間的嵌套容器。
var gray = new PdfGrayColor(75);
var size = new PdfSize(150, 50);
PdfDocumentBuilder.Create().Generate("positioning-minimalbox.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(size);
page.Content().MinimalBox().Background(gray).Text("I don't want more space");
});
doc.Pages(page =>
{
page.Size(size);
page.Content().Background(gray).Text("I'll take everything");
});
});
上述程式碼的結果在 positioning-minimalbox.pdf 中。 由於呼叫了 MinimalBox,第一頁上的文字僅佔據所需的 空間。 在第二頁上,文字覆蓋了整個頁面。
Scale
可以縮放容器中的任何內容。 Scale 方法影響水 平和垂直方向的內容。 使用 ScaleHorizontal 或 ScaleVertical 方法僅在一個方向上更 改內容。 最後兩種方法不保留內容的縱橫比。
小於 1 的比例值會減少容器所佔用的面積。 大於 1 的值會增加面積。 若要翻轉容器中的內容,請使用負縮放
值。 例如,ScaleVertical(-1)
會產生原始內容的顛倒版本。
PdfDocumentBuilder.Create().Generate("positioning-scale.pdf", doc =>
{
doc.Pages(page =>
{
page.Content()
.MinimalBox()
.Column(column =>
{
var scales = new[] { 0.5f, 0.75f, 1, 1.3f, 1.5f };
foreach (var scale in scales)
{
var percent = (int)(scale * 100);
column.Item()
.Scale(scale)
.Text(FormattableString.Invariant($"Scale equals {scale} ({percent}%)."))
.FontSize(20);
column.Item().LineHorizontal(0.5);
}
});
});
});
上述程式碼的結果在 positioning-scale.pdf。
ScaleToFit
您可以縮小內容以適應可用空間。 例如,當您有固定區域來輸出人名或地址。 當然,如果名稱較長,您可以增 加面積。 但更簡單的方法可能是將文字縮小一點。 使用 ScaleToFit 方法縮小內容。
ScaleToFit
保留內容的長寬比。 該方法永遠不會使內容變大。 請注意,此方法執行迭代計算。 這可能會減
慢 PDF 生成過程。
PdfDocumentBuilder.Create().Generate("positioning-scaletofit.pdf", doc =>
{
doc.Pages(page =>
{
page.Content().Column(column =>
{
for (int i = 0; i < 5; i++)
{
column.Item()
.Width(230 - 20 * i)
.Height(20)
.ScaleToFit()
.Border(b => b.Thickness(0.5))
.Text(" This text should fit into the changing width.");
}
});
});
});
上述程式碼的結果在 positioning-scaletofit.pdf。
AspectRatio
縱橫比定義了容器的寬度和高度之間的比例關係。 要找出容器的縱橫比,請將其寬度除以高度。
使用 AspectRatio 方法指定容器的縱橫 比。 當您為不同的佈局或頁面大小設計可重複使用的容器時,它會很有幫助。
PdfDocumentBuilder.Create().Generate("positioning-aspectratio.pdf", doc =>
{
var ratios = new double[] { 0.25, 0.5, 1, 2 };
foreach (var ratio in ratios)
{
var ratioText = ratio.ToString(CultureInfo.InvariantCulture);
doc.Pages(page =>
{
page.Size(200, 200);
page.Content().Column(column =>
{
column.Item()
.AspectRatio(ratio)
.Background(new PdfGrayColor(75))
.Text($"Width / Heigth = {ratioText}");
});
});
}
});
上述程式碼的結果在 positioning-aspectratio.pdf。
此方法具有類型為 AspectRatioMode 的可選參數。 使 用此參數指定如何在保留寬高比的同時調整內容大小。
具有指定長寬比的容器佔用盡可能多的空間。 根據模式,容器將嘗試佔據整個可用區域(預設)、寬度或高度。
請注意,該庫可能會拋出 LayoutException。 當它不能 滿足尺寸、寬高比和寬高比模式要求時,就會發生這種情況。
Unconstrained
容器可以完全沒有尺寸限制。 使用 Unconstrained 方法從容器中刪除所有 大小限制。
不受約束的容器中的內容所佔用的空間大小等於內容的固有大小。 無約束容器本身不佔空間。 因此,同級容器 可以覆蓋不受約束的容器的內容。
PdfDocumentBuilder.Create().Generate("positioning-unconstrained.pdf", doc =>
{
doc.Pages(page =>
{
page.Content().MinimalBox()
.Border(b => b.Thickness(0.5))
.Column(column =>
{
column.Item().Text("First item");
column.Item().Unconstrained()
.Text("Second item ignores all size constraints");
// 第三項使用空行
column.Item().Text(new string(' ', 20))
.BackgroundColor(new PdfRgbColor(187, 237, 237), 50);
column.Item().Text("Fourth item");
});
});
});
上述程式碼的結果在 positioning-unconstrained.pdf 中。 在程 式碼中,我為該列中的第三項使用了一行帶有半透明背景的空格。 正如您所看到的,第三個項目部分涵蓋了第二 個(不受約束)項目。
位置
容器的位置取決於許多因素。 其中一些是對齊、填充、父容器的位置和內容方向。 預設情況下,任何容器都會 黏在最左邊和最上面的可用位置。
Padding
最常見的要求之一是在容器內容周圍添加一些空間。 LayoutContainer 提供了一組方法來設定容器的填充區 域。 填充區域是其內容和邊框之間的空間。 換句話說,填充代表內容周圍的內部空間。
Padding 方法同時設定容器所有四個側面的填
充。 使用 PaddingHorizontal 方
法僅指定左側和右側的填充。
PaddingVertical 僅在頂部和底部執
行相同的操作。 若要在一側單獨設定填充,請使用 PaddingTop/Bottom/Left/Right
方法之一。
Align
若要變更容器的位置,請使用對齊方法。 AlignLeft/AlignCenter/AlignRight
方法應用水平對齊並傳回嵌套
容器。 AlignTop/AlignMiddle/AlignBottom
方法傳回具有相應垂直對齊方式的嵌套容器。
明確應用對齊的容器佔據具有最小所需寬度和/或高度的區域。 以下程式碼會建立一個包含兩個項目的列。 一項 已明確應用對齊。
PdfDocumentBuilder.Create().Generate("positioning-alignment.pdf", doc =>
{
var color = new PdfRgbColor(187, 237, 237);
var text = "Hello";
doc.Pages(page =>
{
page.Size(200, 100);
page.Content().Column(c =>
{
c.Item().Extend().Background(color).Text(text);
c.Item().Extend().AlignLeft().Background(color).Text(text);
});
});
});
Extend 呼叫使兩個項目佔據整個頁面。 我在第 二個項目上調用 AlignLeft 方法。 此呼叫 不會更改位置,因為預設項目的容器將文字左對齊。 但明確應用的對齊方式會更改第二項佔用的區域。
上述程式碼的結果在 positioning-alignment.pdf。
Translate
若要水平和/或垂直重新定位容器,請使用 Translate/TranslateX/TranslateY
方法。 第一個可以水平和垂直
移動容器。 另外兩個僅沿一個方向移動容器。
所有這些方法都會覆蓋位置但保留大小限制。 移動的容器可以與其他容器重疊。 使用負參數值移動到左側和/或 頂部。 正值會導致向右和/或向下移動。
PdfDocumentBuilder.Create().Generate("positioning-translate.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(200, 100);
page.Content().Row(r =>
{
r.ConstantItem(50)
.Background(new PdfRgbColor(187, 237, 237))
.Text("Left");
r.ConstantItem(50)
// 將此物品向左移動 10 點並向下移動 5 點
.Translate(-10, 5)
.Background(new PdfRgbColor(15, 130, 9))
.Text("Right");
});
});
});
上述程式碼的結果在 positioning-translate.pdf。
Rotate
旋轉的內容,尤其是文本,可以透過不同的方式改進您的文件。 例如,您可以節省空間並使文件更具吸引力和美 觀。
LayoutContainer 提供了兩種旋轉內容的方法。 無論您 使用哪種方法,具有旋轉內容的容器都會遵守位置和大小限制。
旋轉 90 度
RotateRight 和 RotateLeft 方法分別將內容順時針和逆時 針旋轉 90 度。
以下程式碼示範如何建立主頁內容旁邊帶有垂直文字的文件。
PdfDocumentBuilder.Create().Generate("positioning-rotate.pdf", doc =>
{
var lightGray = new PdfGrayColor(90);
doc.Pages(page =>
{
page.Size(298, 210);
page.Content().Row(r =>
{
r.AutoItem()
.RotateLeft()
.Background(lightGray)
.Text("This content goes up");
r.RelativeItem(1)
.ExtendVertical()
.PaddingHorizontal(10)
.Column(t =>
{
for (int i = 0; i < 15; i++)
t.Item().Text("The main content line goes here");
});
r.AutoItem()
.RotateRight()
.Background(lightGray)
.Text("This content goes down");
});
});
});
上述程式碼的結果在 positioning-rotate.pdf。
旋轉至任意角度
Rotate 方法將內容旋轉任意度數。 正數導致順 時針旋轉。 負數表示逆時針旋轉。
旋轉原點是容器的左上角。 旋轉的內容可以與其他容器重疊。
PdfDocumentBuilder.Create().Generate("positioning-rotate2.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(298, 210);
page.Content()
.Padding(25)
.Background(new PdfGrayColor(70)) // 灰色的
.AlignCenter()
.AlignMiddle()
.Background(new PdfGrayColor(100)) // 白色
.Rotate(30)
.Width(100)
.Height(100)
.Background(new PdfRgbColor(187, 237, 237)); // 藍色的
});
});
上述程式碼的結果在 positioning-rotate2.pdf。
若要變更旋轉原點,請在呼叫 Rotate 之前呼叫
Translate
方法之一。 不要忘記在呼叫後將原點翻譯回來。
// 平移旋轉原點
.TranslateX(50)
.TranslateY(50)
.Rotate(30)
// 翻譯回來
.TranslateX(-50)
.TranslateY(-50)
條件佈局
PDF 文件的佈局可能取決於條件。 例如,您可以對列中的偶數行和奇數行使用不同的對齊方式或背景顏色。
使用 Container 方法插入巢狀容器,其佈局 取決於條件。 呼叫此方法不會破壞呼叫鏈。
PdfDocumentBuilder.Create().Generate("positioning-container.pdf", doc =>
{
doc.Pages(page =>
{
page.Content().Column(c =>
{
for (int i = 0; i < 15; i++)
{
c.Item()
.TextStyle(TextStyle.Parent.FontSize(14))
.Container(x => i % 2 == 0 ? x.Background(new PdfGrayColor(70)) : x)
.Text($"Row {i + 1}");
}
});
});
});
上述程式碼的結果在 positioning-container.pdf。
DSL
文件的某些部分可以使用相同的佈局。 例如,他們可以設定外觀相同的邊框或使用相同的格式。 根據「不要重 複自己」的原則,我建議將公共程式碼提取到方法中。
對公共程式碼使用擴充方法有兩個好處:
- 您可以在方法呼叫鏈中使用該方法
- 可以為一組方法呼叫一個有意義的名稱
鑑於這些優勢,您可以建立特定於領域的語言 (DSL)。 使用 DSL,您的佈局代碼可以更短且更易於理解。
static class LayoutHelpers
{
public static LayoutContainer NumberCell(this Table table)
=> table.Cell().Border(b => b.Thickness(0.5)).PaddingHorizontal(10);
}
PdfDocumentBuilder.Create().Generate("positioning-dsl.pdf", doc => doc.Pages(page =>
{
page.Content().Table(t =>
{
t.Columns(c =>
{
for (int i = 0; i < 4; ++i)
c.ConstantColumn(50);
});
for (int i = 0; i < 16; i++)
t.NumberCell().Text($"{i + 1}");
});
}));
渲染流程
Layout 外掛程式會根據大小和位置限制排列您放入容器中的任何內容。 有些內容可以跨越多個頁面。 內容的呈 現有嚴格的順序。 內容流是此順序的另一個名稱。
LayoutContainer 提供了一些調整內容流的方法。 您可 能不會每次都需要它們,但在某些情況下沒有其他方法可以實現所需的佈局。
PageBreak
當您需要從新頁面開始內容區塊時,請使用 PageBreak 方法。 例如,您可以使用該方法 從新頁面開始 Column 項目。
下面是一個範例程式碼,該程式碼劃分一列,以便每頁僅包含兩行。
PdfDocumentBuilder.Create().Generate("positioning-pagebreak.pdf", doc => doc.Pages(page =>
{
page.Size(200, 100);
page.Content().Column(c =>
{
for (int i = 1; i <= 10; ++i)
{
c.Item().Text($"Item {i}");
if (i % 2 == 0)
c.Item().PageBreak();
}
});
}));
上述程式碼的結果在 positioning-pagebreak.pdf。
ShowIf
根據條件,您可能需要顯示/隱藏容器。 ShowIf 方法本質上是條件佈局這種特殊情況的語法糖。
在下面的程式碼中,我使用 ShowIf
方法在一行中每 5 個元素後面插入垂直線。
PdfDocumentBuilder.Create().Generate("positioning-showif.pdf", doc => doc.Pages(page =>
{
page.Size(200, 100);
page.Content().Row(r =>
{
for (int i = 0; i < 10; ++i)
{
r.AutoItem().Text(i.ToString());
r.AutoItem().ShowIf(i > 0 && (i + 1) % 5 == 0).LineVertical(0.5);
}
});
}));
上述程式碼的結果在 positioning-showif.pdf。
ShowOnce
您可以防止某段內容在下一頁重複。 使用 ShowOnce 方法指示佈局引擎僅一次完全渲染 內容。
檢查以下程式碼,看看 ShowOnce
如何阻止「Environment」出現在第二頁。
PdfDocumentBuilder.Create().Generate("positioning-showonce.pdf", doc => doc.Pages(page =>
{
page.Size(200, 100);
page.Content().Row(r =>
{
r.RelativeItem()
.Background(new PdfGrayColor(75))
.Border(b => b.Thickness(0.5))
.Padding(5)
.ShowOnce()
.Text("Environment");
r.RelativeItem()
.Border(b => b.Thickness(0.5))
.Padding(5)
.Column(c =>
{
c.Item().Text(Environment.OSVersion.VersionString);
c.Item().Text(string.Empty);
c.Item().Text(
Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER")
?? string.Empty
);
});
});
}));
上述程式碼的結果在 positioning-showonce.pdf。
ShowEntire
預設行為是當一頁內容放不下時在頁面之間拆分內容。 使用 ShowEntire 方法在一頁上呈現整個容器。
請注意,當無法在一頁上容納整個內容時,庫會拋出 LayoutException。
由於以下程式碼中的 ShowEntire
調用,第二項文字從第二頁開始。 如果沒有調用,它將從第一頁開始,緊接
在第一個項目文字之後。
PdfDocumentBuilder.Create().Generate("positioning-showentire.pdf", doc => doc.Pages(page =>
{
page.Size(100, 100);
page.Content().Column(c =>
{
c.Item().Text(t =>
{
for (var i = 0; i < 4; i++)
t.Line($"First item line {i + 1}");
});
c.Item()
.Background(new PdfRgbColor(250, 123, 5))
.ShowEntire()
.Text(t =>
{
for (var i = 0; i < 4; i++)
t.Line($"Second item line {i + 1}");
});
});
}));
上述程式碼的結果在 positioning-showentire.pdf。
EnsureSpace
從某種意義上說,EnsureSpace 是
ShowEntire 方法的一個特例。 不同之處在
於 EnsureSpace
不需要整個內容適合頁面。 此方法僅嘗試使部分內容適合指定的高度。 其他所有內容將在下
一頁進行。
如果目前頁面的未佔用區域的高度小於請求的高度,則整個內容將呈現在新頁面上。 在這裡,該方法將產生與
ShowEntire
方法相同的結果。
StopPaging
使用 StopPaging 方法最多在一頁上產生輸 出。 在容器上呼叫此方法會阻止其分頁。 Layout 附加元件不會在頁面之間分割此容器的內容。 它不會呈現任 何不適合一頁的數據。
以下範例程式碼會為文件新增兩組頁面。 兩個集合都使用工作日名稱列表作為其內容。 第一組只有一個頁面,
因為程式碼為頁面的內容容器呼叫 StopPaging
方法。
PdfDocumentBuilder.Create().Generate("positioning-stoppaging.pdf", doc =>
{
static Action<TextContainer> produceText(string heading)
{
var text = string.Join('\n', DateTimeFormatInfo.InvariantInfo.DayNames);
return t =>
{
t.Line(heading).BackgroundColor(new PdfRgbColor(250, 123, 5));
t.Span(text);
};
}
doc.Pages(page =>
{
page.Size(100, 100);
page.Content()
.StopPaging()
.Text(produceText("Without paging:"));
});
doc.Pages(page =>
{
page.Size(100, 100);
page.Content()
.Text(produceText("Default behaviour:"));
});
});
上述程式碼的結果在 positioning-stoppaging.pdf。
SkipOnce
您可以延後內容的顯示。 如果容器出現在多個頁面上,請使用 SkipOnce 跳過第一頁並在從第二頁開始的所 有頁面上渲染內容。
此功能對於各種標題都很有用,但您也可以將其用於其他內容。 檢查以下程式碼,了解 SkipOnce
如何防止標
題出現在第一頁。
PdfDocumentBuilder.Create().Generate("positioning-skiponce.pdf", doc => doc.Pages(page =>
{
page.Size(298, 210);
page.Header()
.SkipOnce()
.Text("This header will appear starting from page 2")
.Style(TextStyle.Parent.Underline());
page.Content().Column(c =>
{
for (int i = 0; i < 5; i++)
{
if (i > 0)
c.Item().PageBreak();
c.Item().Text($"Page {i + 1}");
}
});
}));
上述程式碼的結果在 positioning-skiponce.pdf。
內容方向
預設內容方向是從左到右。 容器將其文字和其他內容向左對齊。
但有些語言是從右到左書寫的(例如阿拉伯語和希伯來語)。 使用這些語言建立內容時,請使用 ContentFromRightToLeft 方 法。 呼叫它會將容器的內容方向切換為從右到左。 該方法還將切換預設對齊方式。
如果頁面上的大部分內容都是 RTL 語言,則可以將從右到左設定為頁面的預設內容方向。 為此,請使用 PageLayout.ContentFromRightToLeft 方法。 然後,若要覆寫所選容器的預設內容方向,請使用 ContentFromLeftToRight 方 法。
請注意,內容方向不會影響明確指定的對齊方式。 例如,無論您為其容器設定什麼內容方向,右對齊的內容都會 黏在右側。 子元素的視覺順序會根據內容的方向而有所不同。