MyInvois XML 转 PDF——可以交给客户的 A4 发票

读出供应商发来的 UBL XML,先看清楚会印成什么样,再在这个标签页里生成 A4 PDF。XML 不会离开你的浏览器。

🔒 100% 浏览器内运行 · 零服务器上传

QR:无。UUID 和 long ID 是验证时发出的,不在 XML 里,所以不画二维码。

支持 PNG、JPEG、WebP,单边不超过 600 像素。它只保存在这个浏览器里——存在本机,从不上传。

取消勾选就是一份干净的文件——有些买家会这样要求。上面的预览仍然显示标志。

文件里出现中文(或本页使用中文)时,会下载一次 1.3 MB 的字体子集,实际传输约 705 KB。除此之外不下载任何东西,也不上传任何东西。

XML 只在这个标签页里读取。页面没有上传接口;唯一可能发出的请求是页面自己的字体文件,而且只有文件里出现中文时才会发出。

马来西亚规则与特殊情况

这个页面做什么,不做什么

它读取 MyInvois(LHDN)的 UBL 2.1 电子发票 XML,排出一页 A4,看起来就像商家交给客户的那张发票。这只是版面呈现,不是电子发票本身。LHDN 的说明是:电子发票是 XML 或 JSON 文件,不是 PDF、JPG 之类的格式;这里的 PDF 是给客户存档、放进送货单资料夹,或者给柜台那位要纸的人看的。

  • 任何语言都不声称「已验证」。本页从不说文件有效、已获批准或已被 LHDN 接受,不印绿色标记,每份 PDF 的页脚都写着:「本页由浏览器根据供应商的 XML 生成,只是版面呈现,不是经 LHDN 验证的文件。」
  • UUID 不在文件里。IRBM 在验证文件时才发出 UUID 和验证链接,并以 API 元数据的形式返回。它们不是 XML 的元素,所以本页没法替你印出来——你自己粘贴,或者干脆不印。
  • 不上传任何东西。XML 由静态 JavaScript 在这个标签页里读取。页面唯一可能发出的请求是它自己的中文字体文件,而且只有文件里出现汉字时才会发出。

XML 是怎么读的

  • 按命名空间读,不按前缀。开票方会写 cbc:、Cbc: 或 inv: 来表示同一个命名空间,所以每个元素都按命名空间 URI 加本地名匹配。就算 UBLExtensions 里重新声明了默认命名空间也抢不走字段:解析器沿树继承声明,那块里的元素根本不是 UBL 元素。
  • 根元素永远是 Invoice。MyInvois 的八种类型——01 发票、02 贷项通知单、03 借项通知单、04 退款通知单,以及 11–14 的自开类型——根元素都是 Invoice。单据类型来自 cbc:InvoiceTypeCode,不是元素名。根元素是 CreditNote 的文件属于另一种 UBL 文档,会被指名拒绝。
  • 数字先过关。每个金额都按「分」的整数读取。LHDN 自己公布的汇率样例里,PayableAmount 写的是 abc;这种文件会被拒绝,并说出是哪个字段、什么值——印一个 abc 当合计,比不生成 PDF 更糟。

这些坑都处理了,而且看得见

  • 两个分类代码。一行明细可能同时带 CLASS 和 PTC 代码;印的是 CLASS 那个,预览里会说明两个都存在。
  • 两种货币。税额用税务货币表示时,两个数字都印出来,各自带上文件写的货币;汇率照原文印出,不做任何换算。
  • 公众不是买家。TIN EI00000000010(「合并买家」)是 LHDN 给散客的占位号码,会照读并注明是占位,而不是当成已登记买家印出去。
  • 自开 11–14。开出这份文件的一方就是买家,所以两栏标题互换,并写明各自是谁。
  • 超长文字。300 字的说明、150 字的地址行、50 字的城市:都在自己的栏里换行,不会越出页边,一行明细也不会被拆到两页。
  • 文件损坏。错误会说出标签、行号和列号,而不是给你一页空白。

二维码,有才画

两个选填字段接收 MyInvois 在验证通过时返回的 UUID 和 long ID。两个都填了,PDF 上会画出 LHDN 分享链接的二维码;两个都没填,就完全不画——一个扫了什么都不到的二维码,比空着一角更糟。UUID 格式不对会被指名拒绝,让你在打印之前就知道。

标志,想加就加

可以给页眉加一张 PNG、JPEG 或 WebP 图片:按 120 × 30 pt(约 42 × 11 毫米)印出,保持原比例,透明部分照样透明,不会被压成一块灰底。它只记在这个浏览器里——存在本机,从不上传——所以下次回来不用每张发票都重新上传。一个勾选框可以让它不出现在 PDF 里,给那些要求干净文件的买家;取消勾选不会改变上方的预览。SVG 会被指名拒绝:SVG 是存放标记和脚本的容器,不是图片。发票本身的内容,任何时候都不会被存储。

中文内容

文件里出现汉字时,会下载一次 1.3 MB 的字体子集(实际传输约 705 KB),并把整个字体内嵌进去,保证汉字真的画出来而不是被悄悄丢掉。子集里没有的字会被指名拒绝。纯拉丁文的文件完全不下载字体。

处理前后

处理前

<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" …>
  <cbc:ID>INV-2026-1000</cbc:ID>
  <cbc:InvoiceTypeCode>01</cbc:InvoiceTypeCode>
  <cbc:DocumentCurrencyCode>MYR</cbc:DocumentCurrencyCode>

处理后

Invoice / Invois (01) · INV-2026-1000
供应商:Hoelee Trading Sdn Bhd
项目行数:3
合计:RM 4,860.00

使用方法

  1. 把 .xml 文件拖进左侧面板,或者直接粘贴文件内容。不上传任何东西——文件由这个页面自己读取。
  2. 按「检查文件」看结构化预览:单据类型、供应商、买家、每一行明细的数量和金额、税额小计和合计。这时还没有生成任何 PDF。
  3. 有 UUID 和 long ID 就填进去,然后按「生成 A4 PDF」。下载按钮会以发票编号作为文件名。

常见问题

这份 PDF 是 LHDN 官方的电子发票吗?

不是。LHDN 明确说明电子发票是 XML 或 JSON 文件,不是 PDF。本页生成的是给客户存档或打印用的版面呈现,页脚每一页都写着这一点。真正有效的是经 MyInvois 验证的那份 XML 或 JSON。

为什么我的 PDF 上没有二维码?

因为 UUID 和验证链接是 IRBM 在验证时发出的,以 API 元数据返回,并不在 XML 里。你同时填入 UUID 和 long ID 时,才会画出 LHDN 分享链接的二维码;缺一个就什么都不画,而不是画一个扫了没结果的码。

文件被拒绝了,那些错误是什么意思?

提示会说出原因:根元素不对、InvoiceTypeCode 不认识、缺少命名空间、某个字段不是数字、没有明细行,或者标签损坏并给出行号列号。工具不会猜,也不会偷偷修补——修补过的发票已经不是供应商发来的那份文件。

它会判断 LHDN 会不会接受这份文件吗?

不会,也从不这样说。它只读 XML 写明的字段、把关数字,然后印出来。文件是否有效、UUID 是什么,是 IRBM 的答案,不是这一页的。

说明是中文的发票也能印吗?

可以。文件里出现汉字时会下载一次文泉驿字体子集(压缩后约 705 KB)并整个内嵌,汉字就真的印得出来。子集里没有的字会被指名拒绝,不会变成空方框。