NodeJs导出PDF

news2024/9/24 11:23:56
(优于别人,并不高贵,真正的高贵应该是优于过去的自己。——海明威)

在这里插入图片描述

场景

根据订单参数生成账单PDF

结果

在这里插入图片描述

示例代码

/* eslint-disable no-unused-vars */
/* eslint-disable no-undef */
/* eslint-disable complexity */
const PDFDocument = require('pdfkit');
const fs = require('fs');
const dayjs = require('dayjs');
const AgencyWithdrawOrderStatus = {
    Completed: 'Completed',
    Submitted: 'Submitted',
    Rejected: 'Rejected',
}
const agencyName = 'Jasin';
const agencyEmail = 'pdfkit@dev.com';
const order = {
    "id": "FI-AWO-20230718-8657637839",
    "accountID": {
        "id": "ID-AGE-20230707-6351711990",
        "type": "AGENCY"
    },
    "transactionNo": "FIAWO202307180650601146",
    "referenceID": "",
    "transferWay": "Bank card",
    "walletID": "FI-WAL-20230707-5401898911",
    "transferID": "FI-TRF-20230718-4134849682",
    "transactionID": "FI-TRA-20230718-2048728248",
    "somoServiceFee": 345,
    "somoServiceFeeRate": 0.05,
    "bankTransferFee": 3000,
    "bankTransferFeeRate": 3000,
    "paypalTransferFee": 345,
    "paypalTransferFeeRate": 0.05,
    "paypalRemittanceAmount": 6555,
    "bankRemittanceAmount": 6900,
    "totalAmount": 6900,
    "status": "Submitted",
    "transactionList": [{
        "brandName": "asdasdads",
        "transactionID": "FI-TRA-20230710-1494352833",
        "brandID": "ID-BRA-20230627-2309656536",
        "campaignID": "AC-CAM-20230710-3827733853",
        "campaignTitle": "Jack",
        "orderID": "AC-ORD-20230710-2795223591",
        "type": "AGENCY_CAMPAIGN_ORDER_PAYMENT",
        "amount": 6900,
        "somoServiceFee": 345,
        "paypalTransferFee": 345,
        "bankTransferFee": 0
    }, {
        "brandName": "asdasdads",
        "transactionID": "FI-TRA-20230710-5284753385",
        "brandID": "ID-BRA-20230627-2309656536",
        "campaignID": "AC-CAM-20230710-4072616238",
        "campaignTitle": "Kami",
        "orderID": "AC-ORD-20230710-7402268061",
        "type": "AGENCY_CAMPAIGN_ORDER_PAYMENT",
        "amount": 0,
        "somoServiceFee": 0,
        "paypalTransferFee": 0,
        "bankTransferFee": 0
    }],
    "transactionCount": 2,
    "accountBalance": 888750099,
    "withdrawalMethod": {
        "bankEnabled": true,
        "bankAccountName": "12213",
        "bankAccountNumber": "22",
        "bankName": "333",
        "bankAddress": "444",
        "bankSwiftCode": "555"
    },
    "reason": "",
    "createdAt": {
        "$date": "2023-07-18T09:53:45.568Z"
    },
    "updatedAt": {
        "$date": "2023-07-18T09:53:45.568Z"
    },
    "__v": 0
};
const method = async () => {
    const pdfBuffer = await new Promise((resolve) => {
        // 实例化PDF对象
        const doc = new PDFDocument({ size: 'A4' });
        const fontSize = 14;
        const remittanceText = 'Remittance amount:';
        const remittanceTitleColor = '#8792A2';
        const remittanceAmountColor = '#181C1F';
        const remittanceAmountSize = 40;
        const orderStatusText = order.status;
        const orderStatusColor = {
            Submitted: '#4E515C',
            Completed: '#009944',
            Rejected: '#FF0000',
        }[orderStatusText];
        const orderStatusBackColor = {
            Submitted: '#f2f4f9',
            Completed: '#d8f7e6',
            Rejected: '#f0dede',
        }[orderStatusText];
        const orderStatusOffset = {
            Submitted: -6,
            Completed: -8,
            Rejected: 0,
        }[orderStatusText];
        const getFloatNumber = (number) => {
            return parseFloat(number.toString()).toFixed(2);
        };
        const getConvertAmount = (number) => {
            return number / 100;
        };
        const rejectedOffset = order.status === 'Rejected' ? 45 : 0;
        const remittanceAmount = order.withdrawalMethod.paypalEnabled
            ? order.paypalRemittanceAmount
            : order.bankRemittanceAmount;
        const transferFee = order.withdrawalMethod.paypalEnabled
            ? order.paypalTransferFee
            : order.bankTransferFee;
        const remittanceAmountLength = getFloatNumber(
            getConvertAmount(remittanceAmount),
        ).length;
        const remittanceAmoutOffset =
            remittanceAmountLength - 4 > 0 ? remittanceAmountLength - 4 : 0;
        const transferAccount =
            (order.withdrawalMethod.paypalEnabled
                ? order.withdrawalMethod.paypalAccountID
                : order.withdrawalMethod.bankAccountName) ?? ' ';
        const transferDetailTitleColor = '#F2F3F7';
        const transferDetailTitleSize = 16;
        const transferDetailBackgroundColor = '#171A1F';
        const transferDetailTitleText = 'Transfer detail:';

        const orderStatusX = 100;
        const orderStatusY = 105;
        const startX = 73;
        const transferDetailRectY = 150;
        const transferDetailX = 80;
        const transferDetailY = 168;
        const transferDetailWidth = 500;
        const transferDetailHeight = 60;

        const sideWidth = 500;
        const sideHeight = 1;

        const valueColor = '#272A33';
        const linceColor = '#F5F7F7';

        doc.
            // 使用字体
            font(`${__dirname}/Montserrat-Bold.ttf`).
            // 字体大小
            fontSize(fontSize).
            // 字体颜色
            fillColor(remittanceTitleColor).
            // 这一行的文本
            text(remittanceText).
            fontSize(remittanceAmountSize).
            fillColor(remittanceAmountColor).
            text(`$ ${getFloatNumber(getConvertAmount(remittanceAmount))}`).

            // 渲染块,指定x,y坐标以及宽高
            rect(
                orderStatusX + 112 + remittanceAmoutOffset * 23,
                orderStatusY - 8,
                92,
                34,
            ).
            // 块的颜色
            fill(orderStatusBackColor).
            // 块的字体大小
            fontSize(fontSize).
            // 字体的颜色
            fillColor(orderStatusColor).
            font(`${__dirname}/Montserrat-Regular.ttf`).
            // 指定x,y坐标
            text(
                orderStatusText,
                orderStatusX +
                16 +
                112 +
                orderStatusOffset +
                remittanceAmoutOffset * 23,
                orderStatusY,
            );
        if (orderStatusText === AgencyWithdrawOrderStatus.Rejected) {
            doc.
                rect(startX, transferDetailRectY, 469, 30).
                fill('#FFE58F').
                // 插入图片
                image(
                    'withdraw.pdf.rejected.png',
                    startX + 10,
                    transferDetailRectY + 5,
                    { width: 20, height: 20 },
                ).
                fontSize(12).
                fillColor('#000000').
                text(` ${order.reason}`, startX + 35, transferDetailRectY + 6.5);
        }
        doc.
            rect(
                startX,
                transferDetailRectY + rejectedOffset,
                transferDetailWidth,
                transferDetailHeight,
            ).
            fill(transferDetailTitleColor).
            fontSize(transferDetailTitleSize).
            fillColor(transferDetailBackgroundColor).
            font(`${__dirname}/Montserrat-Bold.ttf`).
            text(
                transferDetailTitleText,
                transferDetailX,
                transferDetailY + rejectedOffset,
                {
                    continued: false,
                    baseline: 'top',
                },
            ).

            font(`${__dirname}/Montserrat-Regular.ttf`).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text(
                'Agency:',
                startX,
                transferDetailRectY + transferDetailHeight + 20 + rejectedOffset,
                { lineBreak: false },
            ).
            fillColor(valueColor).

            text(agencyName, startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).

            text(`Email:`, startX, doc.y, { lineBreak: false, lineGap: 5 }).
            fillColor(valueColor).

            text(agencyEmail, startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Transaction No. :', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(order.transactionNo, startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Reference ID. :', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(order.referenceID || ' ', startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Transfer way:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(order.transferWay, startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Transfer account:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(transferAccount, startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Transfer status:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(orderStatusText, startX + 200, doc.y, { lineGap: 5 }).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Created time:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(
                dayjs(order.createdAt.valueOf()).format('YYYY-MM-DD HH:mm:ss'),
                startX + 200,
                doc.y,
                { lineGap: 5 },
            ).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Update time:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(
                dayjs(order.updatedAt.valueOf()).format('YYYY-MM-DD HH:mm:ss'),
                startX + 200,
                doc.y,
                { lineGap: 5 },
            ).
            rect(startX, doc.y + 10, sideWidth, sideHeight).
            fill(linceColor).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Order total amount:', startX, doc.y + 20, { lineBreak: false }).
            fillColor(valueColor).
            text(
                `$ ${getFloatNumber(getConvertAmount(order.totalAmount))}`,
                startX + 200,
                doc.y,
                {
                    lineGap: 5,
                },
            ).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('SOMO service fee:', startX, doc.y, {
                lineBreak: false,
            }).
            fillColor(remittanceTitleColor).
            text(
                `$ ${getFloatNumber(getConvertAmount(order.somoServiceFee))}`,
                startX + 200,
                doc.y,
                {
                    lineGap: 5,
                    strike: true,
                },
            ).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Transfer fee:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(
                `$ ${getFloatNumber(getConvertAmount(transferFee))}`,
                startX + 200,
                doc.y,
                {
                    lineGap: 5,
                },
            ).
            fontSize(fontSize).
            fillColor(remittanceTitleColor).
            text('Remittance amount:', startX, doc.y, { lineBreak: false }).
            fillColor(valueColor).
            text(
                `$ ${getFloatNumber(getConvertAmount(remittanceAmount))}`,
                startX + 200,
                doc.y,
                {
                    lineGap: 5,
                },
            ).
            rect(startX, doc.y + 10, sideWidth, 60).
            fill('#F2F3F7').
            fontSize(transferDetailTitleSize).
            fillColor('#171A1F').
            font(`${__dirname}/Montserrat-Bold.ttf`).
            text('Associated with the order', transferDetailX, doc.y + 30, {
                lineBreak: false,
                baseline: 'top',
            }).
            text(' ', { lineGap: 10 }).
            font(`${__dirname}/Montserrat-Regular.ttf`);

        for (let i = 0; i < order.transactionList.length; i++) {
            const item = order.transactionList[i];
            const itemTransferFee = order.withdrawalMethod.paypalEnabled
                ? item.paypalTransferFee
                : item.bankTransferFee;
            doc.text(' ', { lineGap: 10 });
            doc.
                fontSize(fontSize).
                fillColor(remittanceTitleColor).
                text('Brand:', startX, doc.y, { paragraphGap: -20 }).
                fillColor(valueColor).
                text(item.brandName, startX + 200, doc.y, { lineGap: 8 });

            doc.
                fontSize(fontSize).
                fillColor(remittanceTitleColor).
                text('Campaign:', startX, doc.y, { paragraphGap: -20 }).
                fillColor(valueColor).
                text(item.campaignTitle, startX + 200, doc.y, { lineGap: 8 });

            doc.
                fontSize(fontSize).
                fillColor(remittanceTitleColor).
                text('Order ID:', startX, doc.y, { paragraphGap: -20 }).
                fillColor(valueColor).
                text(item.orderID, startX + 200, doc.y, { lineGap: 8 });

            doc.
                fontSize(fontSize).
                fillColor(remittanceTitleColor).
                text('Oder price:', startX, doc.y, { paragraphGap: -20 }).
                fillColor(valueColor).
                text(
                    `$ ${getFloatNumber(getConvertAmount(item.amount))}`,
                    startX + 200,
                    doc.y,
                    {
                        lineGap: 8,
                    },
                );

            doc.
                fontSize(fontSize).
                fillColor(remittanceTitleColor).
                text('SOMO service fee:', startX, doc.y, {
                    paragraphGap: -20,
                }).
                fillColor(remittanceTitleColor).
                text(
                    `$ ${getFloatNumber(getConvertAmount(item.somoServiceFee))}`,
                    startX + 200,
                    doc.y,
                    {
                        lineGap: 8,
                        strike: true,
                    },
                );

            doc.
                fontSize(fontSize).
                fillColor(remittanceTitleColor).
                text('Transfer fee:', startX, doc.y, { paragraphGap: -20 }).
                fillColor(valueColor).
                text(
                    `$ ${getFloatNumber(getConvertAmount(itemTransferFee))}`,
                    startX + 200,
                    doc.y,
                    {
                        lineGap: 8,
                    },
                );

            doc.rect(startX, doc.y + 20, sideWidth, sideHeight).fill(linceColor);
        }
        doc.end();
        const buffers = [];
        // 使用pdfkit的监听方法收集buffer,便于后续文件处理
        doc.on('data', buffers.push.bind(buffers));
        doc.on('end', () => {
            const pdfData = Buffer.concat(buffers);
            return resolve(pdfData);
        });
    });
    fs.writeFileSync(`${__dirname}/file.pdf`, pdfBuffer);
};
method();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/890465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

忘记LockSupport怎么用了?那我们举个有趣的小例子,永远记住它!

概述 LockSupport是一个非常方便实用的线程阻塞工具&#xff0c;它可以在线程内任意位置让线程阻塞。和Thread.suspend()相比&#xff0c;它弥补了由于resume()在前发生&#xff0c;导致线程无法继续执行的情况。和Object.wait()方法相比&#xff0c;它不需要先获得某个对象的…

用easyui DataGrid编辑树形资料

easyui显示编辑树形资料有TreeGrid元件&#xff0c;但是这个元件的vue版本和react版本没有分页功能。virtual scroll功能也表现不佳。 我用DataGrid来处理。要解决的问题点&#xff1a; &#xff08;1&#xff09;如何显示成树形。即&#xff0c;子节点如何有缩进。 先计算好…

精准无误的公文材料:感谢爱校对软件

在公文处理的过程中&#xff0c;无论是机构还是企业&#xff0c;我们都追求精准无误的结果。在这个信息化、智能化不断发展的时代&#xff0c;爱校对软件以其卓越的性能和优质的服务&#xff0c;赢得了大家的广泛好评。 首先&#xff0c;爱校对软件采用了最新的自然语言处理和…

深入了解msfconsole功能详解

一、前言 正如上篇文章所述&#xff0c;刚开始接触msf&#xff0c;单纯是为了分析某些漏洞&#xff0c;然后在msf中查找相应漏洞软件版本&#xff0c;系统版本的exp便于漏洞分析&#xff0c;同时进行偶尔的exp修改&#xff0c;这就是初期对于msf的使用&#xff0c;以至于我认为…

ai写真制作让你的照片焕发异彩

最近&#xff0c;越来越多的人开始使用ai写真应用程序来美化他们的照片。这些应用程序使用人工智能技术来将人们的照片变成更有艺术感的写真照&#xff0c;是人们的照片看起来更加生动、自然。今天&#xff0c;我将通过几幅生动的ai写真照片&#xff0c;来带你深入探索ai写真ap…

高等数学教材重难点题型总结(三)微分中值定理和导数的应用

第三章&#xff0c;微分中值定理的证明题等&#xff0c;非常重要&#xff0c;需要牢牢掌握 1.证明中值定理对某函数在给定区间上的正确性 2.与中值定理有关的证明题 3.微分中值定理应用于求证不等式 4.洛必达法则求极限 5.洛必达的经典错误反例 6.按某项实现多项式幂展开 7.求带…

外卖订餐系统源码:数字化餐饮新篇章

在当今数字化时代&#xff0c;外卖订餐系统源码成为餐饮行业的一颗明星&#xff0c;为餐厅和顾客提供了无与伦比的便捷体验。在本文中&#xff0c;我们将一起探索一个简单的外卖订餐系统源码示例&#xff0c;了解它是如何将美食带到您的门口的。 # 导入所需模块 import time#…

影响力再度提升,Smartbi多次蝉联Gartner、IDC等权威认可

近期&#xff0c;思迈特软件捷报频传&#xff0c;Smartbi凭借技术创新实力和产品能力&#xff0c;成功入选Gartner中国增强数据分析代表厂商及自助分析代表厂商&#xff0c;同时&#xff0c;连续三年蝉联“IDC中国FinTech 50”榜单。 Part.1 再次被Gartner提名 Smartbi深度融…

实现动画的连续展示 JAVA

目录 1、前言&#xff1a;2、图片的展示以及自动关闭&#xff1a;3、动画的连续展示&#xff1a; 1、前言&#xff1a; 要实现动画的流畅展示需要在能展示图片的基础上对图片进行关闭&#xff0c;再切换下一张图片&#xff0c;这要关闭窗口&#xff0c;与延时函数以及while函数…

FreeModbus——移植(三)

参考自&#xff1a;手把手教你移植FreeModbus到STM32【看评论区引导&#xff0c;领取全套资料包】_freemodbus移植_HQYJ520的博客-CSDN博客 1.准备源码 1.这里用到串口进行传输&#xff0c;所以我没拷贝一个正常的串口工程&#xff08;我用的是正点原子f4库函数版本&#xff…

C++之map的emplace与pair插入键值对用例(一百七十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【Python】解决 pip 开了网络代理之后无法安装包的问题

问题描述 开了网络代理之后&#xff0c;python 的 pip 就无法安装包了&#xff0c;报如下错误&#xff1a; $ pip install netsm Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple WARNING: Retrying (Retry(total4, connectNone, readNone, redirectNone, sta…

爬虫逆向实战(二)--某某观察城市排行榜

一、数据接口分析 主页地址&#xff1a;某某观察 1、抓包 通过抓包可以发现数据接口是multi 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无cookie是否加密&#xff1f; 无响应数据是否加密&#xff1f; 通过查看“响应”板块可以…

Web菜鸟教程 - Springboot接入认证授权模块

网络安全的重要性不言而喻&#xff0c;如今早已不是以前随便弄个http请求就能爬到数据的时代&#xff0c;而作为一个架构师&#xff0c;网络安全必须在产品开发之初就考虑好。因为在产品开发的后期&#xff0c;一方面是客户增多&#xff0c;压力变大&#xff0c;可供利用的时间…

指针作为函数参数间接赋值成立的三个条件,例程,内存调用模型

#include <stdio.h> #include <stdlib.h> int config_read(char * filep, char *** p, int * len) { printf(“config file name is :%s\n”, filep); char ** tempP (char**)malloc(sizeof(char *)*10); for(int i 0; i<10; i) {tempP[i] (char *)malloc(2…

LC-相同的树

LC-相同的树 链接&#xff1a;https://leetcode.cn/problems/same-tree/solutions/363636/xiang-tong-de-shu-by-leetcode-solution/ 描述&#xff1a;给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并…

驱动DAY4 字符设备驱动分步注册和ioctl函数点亮LED灯

头文件 #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t; #define PHY_LED1_ADDR 0X50006000 #define PHY_LED2_ADDR 0X50007000 #d…

Next.js - Pages and Layouts

Pages 页面是路由独有的用户界面。你可以通过从 page.js 文件导出组件来定义页面。使用嵌套文件夹定义路由&#xff0c;并使用 page.js 文件公开访问路由。 // app/page.tsx is the UI for the / URL export default function Page() {return <h1>Hello, Home page!<…

python爬虫7:实战1

python爬虫7&#xff1a;实战1 前言 ​ python实现网络爬虫非常简单&#xff0c;只需要掌握一定的基础知识和一定的库使用技巧即可。本系列目标旨在梳理相关知识点&#xff0c;方便以后复习。 申明 ​ 本系列所涉及的代码仅用于个人研究与讨论&#xff0c;并不会对网站产生不好…

QT学习笔记-Linux ARM环境下实现QT程序通过ODBC驱动访问SQLServer数据库

QT学习笔记-Linux ARM环境下实现QT程序通过ODBC驱动访问SQLServer数据库 0、背景1、基本环境2、搭建交叉编译环境3、在交叉编译服务器上交叉编译安装unixODBC3.1 下载unixODBC3.2 交叉编译unixODBC3.2.1 基本编译说明3.2.2 交叉编译说明3.2.3 ./configure -build,-host,-target…