個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Generate PDFs from declarative XML: text, images, tables, watermarks, headers and footers, with variables, JSON data and loops. A single static Go binary and library.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Generate PDFs from a declarative XML description. You describe pages, text, images, tables, watermarks, headers and footers in XML, and xml2pdf lays them out (wrapping text, breaking pages, repeating table headers) and writes a PDF.
${name}. <for-each> repeats content once for each element of a data array.Documentation: https://sandeepkumarmishra354.github.io/xml-to-pdf/ · Try it in your browser: Playground (sources in docs-src/, rebuilt with make docs)
<document title="Hello" page-size="A4" margin="20mm">
<constants><const name="who" value="World"/></constants>
<page>
<text size="24" bold="true">Hello, ${who}!</text>
</page>
</document>
Linux and macOS (one line):
curl -fsSL https://raw.githubusercontent.com/sandeepkumarmishra354/xml-to-pdf/main/install.sh | sh
The script downloads the right binary for your system from the latest release, verifies its SHA-256 checksum, and installs it to /usr/local/bin. If that directory isn't writable, it uses sudo or falls back to ~/.local/bin. Set XML2PDF_VERSION=0.1.0 to pin a version, or XML2PDF_INSTALL_DIR=~/bin to choose the directory. Installing this way also avoids the macOS "could not verify" warning, because macOS only flags files downloaded through a browser.
Windows, or manual download: get the archive for your platform from the latest release, unpack it, and put xml2pdf on your PATH. On macOS, a binary downloaded through a browser is blocked the first time it runs; clear the flag with xattr -d com.apple.quarantine xml2pdf.
With Go (1.24+):
go install github.com/sandeepkumarmishra354/xml-to-pdf/cmd/xml2pdf@latest
From source:
make build # bin/xml2pdf (static)
make dist # cross-compiled binaries for linux/darwin/windows × amd64/arm64 in dist/
make test # run the test suite
make examples # render examples/*.xml into bin/
xml2pdf invoice.xml # writes invoice.pdf
xml2pdf invoice.xml -o out.pdf --data data.json # values from a JSON file
xml2pdf invoice.xml --json '{"customer":{"name":"Globex"}}' # inline JSON
some-api | xml2pdf invoice.xml --data - # JSON from stdin
xml2pdf invoice.xml --data base.json --json '{"status":"PAID"}' # merged, later wins
xml2pdf invoice.xml --set status=PAID --set n=42 # override single values
cat doc.xml | xml2pdf - -o - > doc.pdf # stdin / stdout
| Flag | Meaning |
|---|---|
-o FILE | Output file. It defaults to the input name with .pdf. Use - for stdout. |
--data FILE | A JSON file containing an object. Its values are available as ${key}, ${a.b} or ${list.0.c}, and its arrays work with <for-each>. Use - to read the JSON from stdin. |
--json JSON | The same as --data, but takes the JSON object inline, e.g. --json '{"name":"Alice"}'. |
--base-dir DIR | The directory that relative image paths resolve against. It defaults to the input file's directory, or the working directory when reading from stdin. |
--set k=v | Sets a variable. It can be repeated and takes priority over --data, --json and constants. |
--version | Prints the version. |
--data and --json can be repeated and mixed. They are deep-merged from left to right: nested objects combine key by key, and anything else (strings, numbers, arrays) is replaced by the later value. So --data invoice.json --json '{"customer":{"name":"Initech"}}' changes only the customer's name and keeps the rest of the file.
import "github.com/sandeepkumarmishra354/xml-to-pdf/pkg/xml2pdf"
data, _ := xml2pdf.LoadData("data.json")
err := xml2pdf.RenderFile("invoice.xml", "invoice.pdf", xml2pdf.Options{
Data: data,
Vars: map[string]string{"status": "PAID"},
})
// or stream: xml2pdf.Render(r io.Reader, w io.Writer, opts)
Lengths are in points (1/72 inch) by default. You can also use mm, cm, in and pt suffixes (20mm, 1.5in). Many attributes accept %:
x is a percentage of the page width and y of the page height.Values are looked up in this order, and the first match wins:
--set key=value on the command line (or Options.Vars)--data and --json (or Options.Data); nested keys use dots, e.g. ${customer.name} or ${items.0.price}<constants> in the documentVariables expand in every attribute and in every piece of text. Constants can reference other variables. An undefined variable is an error that reports its line number. Write $${ to output a literal ${.
<constants>
<const name="company" value="Acme Corp"/>
<const name="footer">© ${year} ${company}</const> <!-- value may also be the element text -->
</constants>
<for-each><for-each> repeats its content once for each element of an array from the data. It's handy for table rows:
<table widths="0.5in,4*,1*">
<row header="true"><cell>#</cell><cell>Item</cell><cell>Price</cell></row>
<for-each items="${items}" var="item" number="n">
<row><cell>${n}</cell><cell>${item.name}</cell><cell>${item.price}</cell></row>
</for-each>
</table>
| Attribute | Meaning |
|---|---|
items | The array to loop over: a variable path such as items, ${items} or ${order.lines}. It's required. |
var | The name for the current element (default item). Use ${item} for a scalar element, or ${item.field} for one field of an object. |
index | Optional name for the 0-based position, e.g. index="i" gives ${i}. |
number | Optional name for the 1-based position, e.g. number="n" gives ${n}. |
Loop variables shadow every other variable with the same name. An empty array produces nothing.
A loop can sit anywhere: around table rows, blocks inside a page, inline text, or whole <page> elements (one page per record, for mail-merge). Loops can be nested:
<for-each items="orders" var="order">
<text style="h2">Order ${order.id}</text>
<for-each items="order.lines" var="line"><text>${line.qty} × ${line.sku}</text></for-each>
</for-each>
When you use the Go library, Options.Data can also hold Go slices and maps, such as []map[string]any, not only decoded JSON.
Content flows from top to bottom inside the page margins and continues on new pages as needed. Paragraphs break between lines and tables break between rows; a table's header rows repeat on each new page. Give any block an x and/or y to position it absolutely, measured from the page's top-left corner. Absolute blocks don't affect the flow.
Each <page> element starts a new physical page, and its overflow pages share its settings.
Text properties cascade from document to page to block to inline span:
| Attribute | Values | Inherited |
|---|---|---|
style | one or more named styles, e.g. style="h1 muted" | – |
font | helvetica (also arial, sans), times (serif), courier (monospace), zapfdingbats | yes |
size / font-size | font size, e.g. 11 | yes |
bold, italic, underline | true / false | yes |
color | #rgb, #rrggbb, rgb(r,g,b) or a name (red, gray, navy, …) | yes |
align | left, center, right, justify | yes |
line-height | a multiple of the font size (default 1.2) | yes |
margin-top, margin-bottom | space above and below a block | no |
bg | background color (rows and cells) | no |
Named styles are declared in <resources>. A style can extends another one, and the attributes written on the element itself win over the named style.
<document>The root element. Its attributes are title, author, subject, keywords (PDF metadata), page-size (A3, A4, A5, Letter, Legal, Tabloid, or WxH such as 100mmx150mm), orientation (portrait or landscape), margin (1–4 values, CSS order: top right bottom left; default 0.5in), and any style attribute.
Its children are <constants>, <resources>, <watermark>, <header>, <footer> and <page>.
<resources><resources>
<image id="logo" src="images/logo.png"/> <!-- PNG, JPEG or GIF; path relative to the XML file -->
<style id="h1" size="20" bold="true" color="#1f4e9c" margin-bottom="6"/>
<style id="h2" extends="h1" size="14"/>
</resources>
<page>Optional overrides: page-size, orientation, margin. Use header="false", footer="false" or watermark="false" to hide those on this page and its overflow pages. Style attributes cascade to the content.
Its children are any blocks. Loose text placed directly inside a page becomes a paragraph.
<text> (or <p>) is a paragraph. Whitespace is collapsed and lines wrap automatically. Inline markup: <b>/<strong>, <i>/<em>, <u>, <span …style attributes…>, <br/>.
<text align="justify" margin-bottom="8">Plain, <b>bold</b>, <span color="red" size="14">big red</span><br/>new line</text>
<text x="120mm" y="20mm" width="70mm" align="right">Absolutely placed</text>
<img> (or <image>) takes ref="id" or src="file.png". Set width and/or height; if you set only one, the aspect ratio is kept. Images never grow beyond the available space. align places the image horizontally, and x/y positions it absolutely.
<table>
<table widths="3*,1*,80,20%" border="0.5" border-color="#bbb" padding="4 6"
header-bg="#eee" stripe="#f7f7f7" repeat-header="true">
<row header="true"><cell>Item</cell><cell align="right">Qty</cell>…</row>
<row><cell colspan="2">…</cell><cell valign="middle">…</cell>…</row>
</table>
| Attribute | Meaning |
|---|---|
widths | Column widths. N* takes a share of the remaining space, a plain length is a fixed width, and % is a percentage of the table width. Without widths, all columns are equal. |
width | Table width (default 100%). |
border, border-color | Cell border width (default 0.5; 0 means none) and color. |
padding | Cell padding, 1–4 values (default 4). |
header-bg, stripe | Background of header rows, and of every other body row. |
repeat-header | Repeat header rows after a page break (default true). |
<row> takes header="true" (bold, and repeated across pages), height (minimum height), bg, and style attributes. <cell> takes colspan, valign (top, middle or bottom), bg, and style attributes. A cell can hold inline text or blocks (<text>, <img>, even a nested <table>). Rows are never split across pages.
<spacer height="10mm"/> adds vertical space. <page-break/> starts a new page.
<header> / <footer>Content repeated on each page, inside the top or bottom margin box. The body content area shrinks to make room.
{page} and {pages} in their text become the current page number and the total page count.height sets a fixed height; by default the height is measured from the content.gap is the space between the band and the body (default 6).pages selects which pages show the band.<footer pages="all"><text align="center" size="9">Page {page} of {pages}</text></footer>
<header pages="2-"><text align="right">Annual report</text></header>
<watermark>A text or image watermark. You can declare several.
<watermark text="DRAFT" opacity="0.15" rotate="45" size="80" color="red"/>
<watermark image="logo" width="60%" opacity="0.06" layer="back" pages="2-"/>
| Attribute | Meaning |
|---|---|
text / image / src | The content: text, an image resource id, or an image file. |
opacity | 0–1 (default 0.15). |
rotate | Degrees counter-clockwise (default 45). |
layer | front (default) draws over the content; back draws beneath it. |
x, y | Center point (default: page center). |
width, height | Image size (default: 50% of the page width). |
pages | Page selector (see below). |
| style attributes | font, size (default 60), bold (default true), color (default gray). |
The pages attribute on header, footer and watermark is a comma-separated list of all, first, last, odd, even, 3, 2-5 or 2- (page 2 onwards). Page numbers count physical pages in the output.
<if>) yet.cmd/xml2pdf CLI
pkg/xml2pdf public Go API
internal/parser XML → node tree (keeps mixed content and line numbers)
internal/vars ${...} resolution
internal/model typed document model + validation
internal/style colors, fonts, named styles and the cascade
internal/units length parsing
internal/layout text wrapping, tables, pagination → drawing ops
internal/render drawing ops → PDF (go-pdf/fpdf)
examples/ invoice.xml (+ invoice.json), report.xml
Releases are built by GoReleaser in GitHub Actions. To publish one, push a version tag:
git tag v0.1.0
git push origin v0.1.0
The release workflow runs the tests, cross-compiles the binaries, and publishes a GitHub Release with archives, checksums and a changelog. Run make snapshot to try the release build locally; it needs goreleaser installed and writes to dist/.
MIT: free for personal and commercial use.