@gummylab/dime-pdf-extract
v1.0.0
Published
Extract structured trading data from KKP Dime securities tax invoices (PDF)
Downloads
17
Maintainers
Readme
@gummylab/dime-pdf-extract
Extract structured trading data from KKP Dime securities tax invoices (PDF) — the confirmation note / receipt / tax invoice (ใบยืนยันการซื้อขาย / ใบเสร็จรับเงิน / ใบกำกับภาษี).
Returns a typed DimeInvoice object (data + helper methods). No CLI, no config — just one function.
Requirements
- Node.js ≥ 22 (unpdf/pdfjs requirement) or Bun
- Windows / macOS / Linux
Install
npm install @gummylab/dime-pdf-extract
# or
bun add @gummylab/dime-pdf-extractQuick Start
import { extractDimeInvoice } from "@gummylab/dime-pdf-extract";
// Bun
const buffer = await Bun.file("input/test.pdf").arrayBuffer();
// Node
// import { readFile } from "node:fs/promises";
// const buffer = await readFile("input/test.pdf");
const invoice = await extractDimeInvoice(new Uint8Array(buffer), {
password: "your-password",
});
console.log(invoice.customer.name); // "นายสมชาย ใจดี" (synthetic sample)
console.log(invoice.getTransactions("SEL")); // sell trades only
console.log(invoice.getHoldings()); // units + avg cost per security
console.log(JSON.stringify(invoice)); // plain data via toJSON()API
extractDimeInvoice(buffer, options?)
| Param | Type | Required | Description |
|---|---|---|---|
| buffer | Uint8Array | Yes | Raw PDF bytes (encrypted or not) |
| options.password | string | No | User password for encrypted PDFs |
| options.sourceName | string | No | Source label stored in invoice.source (default "document") |
Returns: Promise<DimeInvoice>
Errors:
| Error | When |
|---|---|
| PasswordException — "Incorrect Password" | Wrong or missing password |
| InvalidPDFException — "Invalid PDF structure." | Corrupt input or not a PDF |
DimeInvoice
Readonly data properties: source, pageCount, documentType, issuer, branch, registrationNo, taxId, taxPayerId, customer, account, documentNumbers, dates, transactions, totals, transactionTypeLegend, offshoreAccountNo, legalNote, rawText.
| Method | Returns | Description |
|---|---|---|
| toJSON() | DimeInvoiceData | Plain data shape — used automatically by JSON.stringify; for storage / APIs |
| getTransactions(type?) | Transaction[] | Filter by type (BUY / SEL / REW / ...); no arg = copy of all |
| getHoldings() | Holding[] | Units per security (BUY − SEL) + average cost in USD |
Holding: { security, exchange, units, avgCostUsd } — avgCostUsd is null when the invoice has no BUY for that security (cost basis would come from earlier invoices).
Note:
JSON.parse(JSON.stringify(invoice))yields a plain object, not aDimeInvoiceinstance. Keep the instance in memory; usetoJSON()output for storage.
Output structure
All values below are synthetic sample data — real invoices contain personal financial data.
{
"source": "sample.pdf",
"pageCount": 2,
"documentType": "TaxInvoice",
"issuer": "บริษัทหลักทรัพย์ เคเคพี ไดม์ จำกัด",
"branch": { "code": "00000", "name": "สาขาสำนักงานใหญ่" },
"registrationNo": "0105564162001",
"taxId": "0105564162001",
"taxPayerId": "1910600000001",
"customer": { "name": "นายสมชาย ใจดี", "address": "123 ถนนสุขุมวิท กรุงเทพฯ 10110" },
"account": { "no": "00000000001", "type": "Limited Margin Account" },
"documentNumbers": { "no": "2025061900009999", "taxInvoiceNo": "DIMEOS20250619009999" },
"dates": {
"issueDate": "19/06/2025",
"effectiveDate": "18/06/2025",
"settlementDate": { "th": "18 มิถุนายน 2568", "en": "18 June 2025" }
},
"transactions": [
{
"orderId": "888111",
"settlementDate": "20/06/2025",
"transactionType": "BUY",
"exchange": "XNAS",
"security": "TSLA",
"unit": 10,
"unitPrice": 10,
"currency": "USD",
"grossUsd": 100, "feeUsd": 1.5, "withholdingTaxUsd": 0, "totalUsd": 101.5,
"grossThb": 3200, "feeThb": 48, "withholdingTaxThb": 0, "totalThb": 3248
}
],
"totals": {
"totalBuyThb": 200, "totalSellThb": 50, "totalFeeExcludeVat": 1.25,
"discountThb": -0.25, "totalFeeAfterDiscount": 1, "totalVat": 0.07,
"withholdingTax": 0, "fxRateThbPerUsd": 32
},
"transactionTypeLegend": { "BUY": "ซื้อหลักทรัพย์ (Buy)", "SEL": "ขายหลักทรัพย์ (Sell)" },
"offshoreAccountNo": "00000000001",
"legalNote": "…legal paragraphs…",
"rawText": { "page1": "…", "page2": "…" }
}Notes:
rawTextkeeps the raw text of every page — if a field fails to parse, the data is never silently lost.transactions[].*Thb= THB amounts as shown in the document (USD × FX rate).- Negative numbers appear as
(0.25)in the document → parsed as-0.25. transactionType:BUY/SEL(sell) /REW/EXC/EXP— verified against real BUY and SELL samples.
Development
See docs/DEVELOPMENT.md — build, test, publish.
Limitations
- The parser is tuned to the KKP Dime invoice layout — other documents / layouts may yield partial fields (they fall back to
rawText). - Uses unpdf (pdfjs) — handles encrypted PDFs (RC4/AES) and broken xref tables. PDFs with digital signatures + incremental updates (like Dime invoices) fail on MuPDF but parse fine here.
- Tested against real BUY and SELL samples; other transaction types (
REW/EXC/EXP) are parsed but not yet verified against real documents.
