# Itext Pdf 5 教程

news2024/7/3 16:09:49

Itext Pdf 5 教程

Itext Pdf

  • Itext7收费,故使用Itext5传统版,Itext5不再维护

  • 官网:iText 5 |iText PDF
    在这里插入图片描述

  • Itext5 Java Api 地址:iText 5 Java | iText PDF

依赖

<!-- itextpdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.3</version>
</dependency>
<!-- pdf 中文支持 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>
<!--itext 转化pdf -->
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>core-renderer</artifactId>
    <version>R8</version>
</dependency>
<!-- 单元格样式等属性 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>layout</artifactId>
    <version>7.2.3</version>
</dependency>

本文说明

  • 使用com.itextpdf.kernelcom.itextpdf.layout的相关属性进行操作Pdf
  • 使用 com.itextpdf.text 操作 PDF 文档请看 https://blog.csdn.net/qq_37248504/article/details/124224968
  • 两种方式相关API不同

效果展示

在这里插入图片描述

完整测试代码见

  • https://gitee.com/Marlon_Brando/JavaTest/tree/develop/src/main/java/otherapi/itext

常用API

创建Document

/**
 * 获取 Document
 *
 * @param byteArrayOutputStream byte array output stream
 * @return Document
 */
private Document getDocument(ByteArrayOutputStream byteArrayOutputStream) {
    PdfDocument pdfDoc = new PdfDocument(new PdfWriter(byteArrayOutputStream, new WriterProperties().setFullCompressionMode(true)));
    // 创建文档对象 设置文档大小为 A4
    Document document = new Document(pdfDoc, PageSize.A4);
    // 设置 document 的 margin
    document.setMargins(10, 10, 10, 10);
    return document;
}

中文字体设置

  • iText-Asian包支持的中文字体如下图
  • iText-Asian包支持的中文字体只有简体的STSong华文宋体和三种繁体
    在这里插入图片描述

引入其它字体

  • 注意不同版本创建字体api不同但是大致一样
// 获取中文字体
PdfFont chineseFont = PdfFontFactory.createFont("src/main/resources/fonts/STSong.ttf", PdfEncodings.IDENTITY_H);
// 获取英文字体
PdfFont englishFont = PdfFontFactory.createFont(FontFactory.TIMES_ROMAN);
  • 注意创建的字体做缓存,如果每次都创建会导致Pdf文件过大

添加段落

/**
 * 获取段落
 *
 * @return Paragraph
 * @throws IOException Io exception
 */
private Paragraph getParagraph() throws IOException {
    // 获取中文字体
    PdfFont chineseFont = PdfFontFactory.createFont("src/main/resources/fonts/STSong.ttf", PdfEncodings.IDENTITY_H);
    // 获取英文字体
    PdfFont englishFont = PdfFontFactory.createFont(FontFactory.TIMES_ROMAN);
    Paragraph englishText = new Paragraph("Hello world!");
    englishText.setFont(englishFont);
    Paragraph chineseText = new Paragraph("你好早安!");
    chineseText.add(englishText);
    chineseText.setFont(chineseFont);
    chineseText.setBorder(getSolidBorder(ItextPdfConst.BLACK));
    return chineseText;
}

字体设置颜色

chineseText.setFontColor(ColorConstants.RED, 2);

Hello World 示例

  • 中文字体使用宋体、英文字体使用新罗马
    在这里插入图片描述
@Test
public void test4() throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    Document document = getDocument(byteArrayOutputStream);
    OutputStream out = new FileOutputStream(FILENAME);
    document.add(getParagraph());
    document.close();
    byteArrayOutputStream.writeTo(out);
}

添加表格

  • 创建10列的表格
/**
 * 获取表格
 *
 * @return IBlockElement
 * @throws IOException io Exception
 */
private IBlockElement getTable() throws IOException {
    Table table = new Table(ItextKernelUtils.TABLE_PROPORTION);
    table.setMargins(5, 5, 5, 5);
    List<Cell> tableCell = getTableCell();
    for (Cell cell : tableCell) {
        table.addCell(cell);
    }
    return table;
}
/**
 * 获取表格中的单元格
 *
 * @return List
 * @throws IOException Io Exception
 */
private List<Cell> getTableCell() throws IOException {
    List<Cell> cellList = new ArrayList<>();
    Cell cell;
    for (int i = 0; i < 20; i++) {
        cell = new Cell();
    cell.add(getCellParagraph(i + 1));
        cellList.add(cell);
    }
    return cellList;
}
  • 文档增加表格
document.add(getTable());

图片打印

/**
 * 获取图片
 *
 * @return Cell
 * @throws MalformedURLException Exception
 */
private Cell getPicture() throws MalformedURLException {
    Cell cell = new Cell();
    Image image = new Image(ImageDataFactory.create("src/main/resources/picture/one.jpg"));
    // 设置等比例缩放
    image.setAutoScale(true);
    cell.add(image);
    return cell;
}
document.add(getPicture());

Pdf添加水印

  • 效果图如下
    在这里插入图片描述

单一水印

/**
 * Pdf 添加水印
 *
 * @param sourceFile 源文件
 * @param targetFile 目标文件
 * @param waterMark  水印
 * @throws IOException       Io exception
 * @throws DocumentException document exception
 */
public static void addWatermark(String sourceFile, String targetFile, String waterMark) throws IOException, DocumentException {
    // 待加水印的文件
    PdfReader reader = new PdfReader(new FileInputStream(sourceFile));
    // 加完水印的文件
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(targetFile));
    // 设置字体
    BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
    // 设置透明度
    PdfGState gs = new PdfGState();
    // pdf页数
    int pageCount = reader.getNumberOfPages() + 1;
    PdfContentByte content;
    // 循环对每页插入水印
    for (int i = 1; i < pageCount; i++) {
        // 水印的起始
        content = stamper.getOverContent(i);
        gs.setFillOpacity(0.5f);
        content.setGState(gs);
        // 开始
        content.beginText();
        // 设置颜色 默认为黑色
        content.setColorFill(BaseColor.LIGHT_GRAY);
        // 设置字体及字号
        content.setFontAndSize(baseFont, 50);
        // 开始写入水印
        content.showTextAligned(Element.ALIGN_BASELINE, waterMark, 180, 340, 45);
        content.endText();
    }
    stamper.close();
    reader.close();
}

满屏水印

/**
 * 水印填满整个页面
 *
 * @param stamper       输出已添加水印的文件
 * @param waterMarkName 水印文字
 */
public static void fillUpWaterMark(PdfStamper stamper, String waterMarkName) {
    try {
        BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Rectangle pageRect;
        PdfGState gs = new PdfGState();
        // 设置透明度
        gs.setFillOpacity(0.5f);
        gs.setStrokeOpacity(0.5f);
        int total = stamper.getReader().getNumberOfPages();
        JLabel label = new JLabel();
        FontMetrics metrics;
        label.setText(waterMarkName);
        metrics = label.getFontMetrics(label.getFont());
        int textH = metrics.getHeight();
        int textW = metrics.stringWidth(label.getText());
        PdfContentByte under;
        for (int i = 1; i <= total; i++) {
            pageRect = stamper.getReader().getPageSizeWithRotation(i);
            under = stamper.getOverContent(i);
            under.saveState();
            under.setGState(gs);
            under.beginText();
            // 设置水印字体大小颜色
            under.setFontAndSize(base, 20);
            under.setColorFill(BaseColor.RED);
            // 水印文字成30度角倾斜
            float height1 = pageRect.getHeight();
            for (int height = 5 + textH; height < height1; height = height + textH * 3) {
                float width1 = pageRect.getWidth();
                for (int width = 5 + textW; width < width1 + textW; width = width + textW + 3) {
                    under.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 45);
                }
            }
            // 添加水印文字
            under.endText();
        }
        stamper.close();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
}

增加一个新区域

/**
  * 增加新的一页
  * @return
  */
private AreaBreak getAreaBreak() {
    AreaBreak areaBreak = new AreaBreak();
    areaBreak.setPageSize(PageSize.A5);
    return areaBreak;
}
// document.add(getAreaBreak());

增加列表

  • 效果如下
    在这里插入图片描述
/**
  * 获取列表
  *
  * @return List
  */
private com.itextpdf.layout.element.List getList() {
    com.itextpdf.layout.element.List list = new com.itextpdf.layout.element.List();
    // Add elements to the list
    list.add("Java");
    list.add("JavaFX");
    list.add("Apache Tika");
    list.add("OpenCV");
    list.add("WebGL");
    list.add("Coffee Script");
    list.add("Java RMI");
    list.add("Apache Pig");
    return list;
}
// document.add(getList());

合并拆分Pdf

  • ItextMergeTest.java
public class ItextMergeTest {


    private static final String FILE_NAME1 = "demo.pdf";
    private static final String FILE_NAME2 = "demo1.pdf";
    private static final String DEST = "MergeDemo.pdf";
    private static final String DEST_FOLDER = "./file/pdftest";

    /**
     * 使用 copyPagesTo 合并
     *
     * @throws IOException ioexception
     */
    @Test
    public void test1() throws IOException {
        PdfWriter pdfWriter = new PdfWriter(DEST);
        // 最终合并的 dpf
        PdfDocument pdfDocument = new PdfDocument(pdfWriter);

        PdfReader pdfReader1 = new PdfReader(FILE_NAME1);
        PdfDocument pdfDocument1 = new PdfDocument(pdfReader1);
        pdfDocument1.copyPagesTo(1, pdfDocument1.getNumberOfPages(), pdfDocument);

        PdfReader pdfReader2 = new PdfReader(FILE_NAME2);
        PdfDocument pdfDocument2 = new PdfDocument(pdfReader2);
        pdfDocument2.copyPagesTo(1, pdfDocument2.getNumberOfPages(), pdfDocument);

        pdfReader1.close();
        pdfDocument1.close();
        pdfReader2.close();
        pdfDocument2.close();
        pdfDocument.close();
    }

    /**
     * 使用  pdfMerger.merge
     *
     * @throws IOException ioexception
     */
    @Test
    public void test2() throws IOException {
        PdfWriter pdfWriter = new PdfWriter(DEST);
        // 最终合并的 dpf
        PdfDocument pdfDocument = new PdfDocument(pdfWriter);
        PdfMerger pdfMerger = new PdfMerger(pdfDocument);

        PdfReader pdfReader1 = new PdfReader(FILE_NAME1);
        PdfDocument pdfDocument1 = new PdfDocument(pdfReader1);
        pdfMerger.merge(pdfDocument1, 1, pdfDocument1.getNumberOfPages());

        PdfReader pdfReader2 = new PdfReader(FILE_NAME2);
        PdfDocument pdfDocument2 = new PdfDocument(pdfReader2);
        pdfMerger.merge(pdfDocument2, 1, pdfDocument2.getNumberOfPages());

        pdfReader1.close();
        pdfDocument1.close();
        pdfReader2.close();
        pdfDocument2.close();
        pdfDocument.close();
    }

    /**
     * 每个文件 1 页 分割
     *
     * @throws IOException ioexception
     */
    @Test
    public void test3() throws IOException {
        pdfSplitter(DEST, 1, DEST_FOLDER);
    }


    /**
     * 在指定目录等分pdf
     *
     * @param fileName 要分割的文档
     * @param pageNum  分割尺寸
     * @param desDir   分割后存储路径
     * @throws IOException ioexception
     */
    private void pdfSplitter(String fileName, Integer pageNum, String desDir) throws IOException {
        PdfReader pdfReader = new PdfReader(fileName);
        PdfDocument pdf = new PdfDocument(pdfReader);
        String name;
        PdfWriter pdfWriter;
        PdfDocument pdfWriterDoc;
        int numberOfPages = pdf.getNumberOfPages();
        for (int i = 1; i <= numberOfPages; i += pageNum) {
            name = desDir + "/" + i + ".pdf";
            File file = new File(name);
            if (!file.exists()) {
                file.createNewFile();
            }
            pdfWriter = new PdfWriter(name);
            pdfWriterDoc = new PdfDocument(pdfWriter);
            int end = Math.min((i + pageNum - 1), pdf.getNumberOfPages());
            //从页数第一页开始,
            pdf.copyPagesTo(i, end, pdfWriterDoc);
            pdfWriterDoc.close();
            pdfWriter.close();
        }
        //关闭
        pdf.close();
        pdfReader.close();

    }

    /**
     * 分割文档,分割后文仔默认存储在原来的文档路径下。
     *
     * @param fileName
     * @param pageNum
     * @throws IOException
     */
    private void pdfSplitter(String fileName, Integer pageNum) throws IOException {
        String desDir = new File(fileName).getParentFile().getAbsolutePath();
        pdfSplitter(fileName, pageNum, desDir);
    }

    /**
     * 返回自定义片段大小的文件,UUID名称命名。
     *
     * @param fileName
     * @param startPage
     * @param endPage
     * @throws IOException
     */
    public void pdfSplitter(String fileName, int startPage, int endPage) throws IOException {
        //源文档
        PdfReader pdfReader = new PdfReader(fileName);
        PdfDocument pdf = new PdfDocument(pdfReader);
        //目标文档名
        String desDir = new File(fileName).getParentFile().getAbsolutePath() + "/" + UUID.randomUUID() + ".pdf";
        //生成目标文档
        PdfWriter pdfWriter = new PdfWriter(desDir);
        PdfDocument outPdfDocument = new PdfDocument(pdfWriter);
        //从页数第一页开始,
        pdf.copyPagesTo(startPage, endPage, outPdfDocument);
        //关闭
        outPdfDocument.close();
        pdfWriter.close();
        pdf.close();
        pdfReader.close();
    }

}

添加页眉

  • 手动控制页眉页脚的位置添加元素,com.itextpdf.text.pdf会使用事件的方式添加
/**
 * 获取页眉
 *
 * @param document document
 * @return Cell
 * @throws IOException ioexception
 */
private Cell getPdfHeader(Document document) throws IOException {
    Cell cell = new Cell();
    // 获取中文字体
    PdfFont chineseFont = PdfFontFactory.createFont("src/main/resources/fonts/STSong.ttf", PdfEncodings.IDENTITY_H);
    Paragraph chineseText = new Paragraph("我是页眉");
    chineseText.setFont(chineseFont);
    cell.setHorizontalAlignment(HorizontalAlignment.CENTER);
    cell.setVerticalAlignment(VerticalAlignment.MIDDLE);
    cell.setTextAlignment(TextAlignment.CENTER);
    cell.add(chineseText);
    cell.setHeight(30);
    float documentAvailableHeight = ItextKernelUtils.getDocumentAvailableHeight(document);
    float value = cell.getHeight().getValue();
    ItextKernelUtils.setDocumentAvailableHeight(document, documentAvailableHeight - value);
    return cell;
}

设置获取文档高度

public class DocumentPropertyConst {
    /**
     * 当前document可用高度
     */
    public static final Integer AVAILABLE_HEIGHT = 8188;
    public static final Integer PRINT_LEVEL = 8189;
    public static final Integer HASHEADER = 8190;
    public static final Integer HASFOOTER = 8191;
    public static final Integer SIKP_HEADER_FOOTER = 8192;
}

/**
 * 设置当前打印页可用高度
 *
 * @param document document
 * @param height   height
 */
public static void setDocumentAvailableHeight(Document document, float height) {
    document.setProperty(DocumentPropertyConst.AVAILABLE_HEIGHT, height);
}

/**
 * 获取文档实际高度
 *
 * @param document document
 * @return float
 */
public static float getDocumentAvailableHeight(Document document) {
    return document.getProperty(DocumentPropertyConst.AVAILABLE_HEIGHT);
}

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

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

相关文章

视频编辑:VisioForge Video Edit SDK .Net 15.5 标准版 Crack

视频编辑&#xff1a;VisioForge Video Edit SDK .Net v15.5 标准版 Crack,我没有专业版&#xff0c;希望你提供 VisioForge Video Edit SDK .Net 允许程序员轻松地将视频编辑和处理功能集成到他们的软件应用程序中。SDK 允许您使用任何音频和视频文件创建您的电影。您可以为其…

2022年游戏安全风险增长96%,高维作弊对抗激烈

导读&#xff1a;2022年&#xff0c;游戏行业在多种因素影响下遭遇寒冬。但游戏黑灰产规模在迅速壮大&#xff0c;不少游戏饱受其侵扰&#xff0c;越来越多的游戏厂商开始重视游戏安全问题。 为帮助游戏厂商能够清晰、直观地了解当前游戏安全对抗形势&#xff0c;在经过多轮调…

【虹科云展厅专题】虹科赋能汽车智能化云展厅——汽车总线专题

虹科2023年开年福利 聚焦前沿技术&#xff0c;【虹科赋能汽车智能化云展厅】正式上线&#xff0c;本次云展厅围绕“汽车以太网/TSN、汽车总线、智能网联、电子测试与验证、自动驾驶”等核心话题&#xff0c;为您带来如临展会现场般的讲演与介绍&#xff0c;更有技术工程师全程…

机智云DUT实现远程智能鱼池管理系统

一、前言机智云研发的4G DTU在养殖智能系统管理方面优势&#xff0c;此处以智能鱼池管理系统为例。1.实时数据监控机智云智能鱼池养殖智能系统实现对整个鱼池水质的实时监控&#xff0c;无人值守设备状态下运行。为客管理者提供实时异常数据报警信息、实时环境参数波动、实时/定…

nacos的安装部署

文章目录1.nacos的下载2.nacos数据库的建立3.nacos配置文件修改3.1配置文件3.2设置单点登录4.登录nacos网页1.nacos的下载 下载地址&#xff1a; https://github.com/alibaba/nacos/releases 根据自己的需要下载合适的版本。 2.nacos数据库的建立 navicat运行nacos下conf的…

【深度】从链上分析和金融安全角度,看stETH的囚徒困境和Celsius挤兑事件

6月7日开始&#xff0c;以Celsius被曝损失 3.5 万枚 ETH开始&#xff0c;ETH流动性生态乃至整个加密货币市场进入到一个以stETH为中心的流动性囚徒困境之中&#xff0c;而这也造成了对Celsius等加密货币“银行”挤兑现象的发生。 SharkTeam将从事件的起源开始&#xff0c;也就…

拆解“算力偏科”难题,智算中心下一步向何处去?

十年前&#xff0c;英国《经济学人》曾用工业用电量为主的指标来评估中国GDP&#xff0c;而现在算力已经成为新的指标。似乎每个企业、每个城市都在努力增加算力。一位读者不无困惑地留言&#xff0c;大家都说自己算力有多少FLOPS&#xff0c;能支撑这个大模型、那个大数据&…

九龙证券|北上资金连续10日“跑步入场”,1月净流入已逼近2022全年

1月以来&#xff0c;北上资金净流入规划已接近2022全年。 半导体概念股集体大涨 1月17日&#xff0c;沪指缩量小幅调整&#xff0c;收跌0.1%&#xff1b;深成指涨0.13%&#xff0c;创业板指收涨0.24%&#xff0c;科创50涨逾1%。 板块方面&#xff0c;半导体及元件板块继续走强…

基于.NetCore+React单点登录系统

更多开源项目请查看&#xff1a; 一个专注推荐.Net开源项目的榜单 对于有多个应用系统的企业来说&#xff0c;每一个应用系统都有自己的用户体系&#xff0c;这就造成用户在切换不同应用系统时&#xff0c;就要多次输入账号密码&#xff0c;导致体验非常不好&#xff0c;也造成…

【vue系列-07】vue脚手架的基本使用

深入理解脚手架的使用一&#xff0c;vue脚手架的基本使用1&#xff0c;vue-cli安装2&#xff0c;vue项目中的文件组成3&#xff0c;render配置项4&#xff0c;ref属性5&#xff0c;props属性6&#xff0c;mixin属性7&#xff0c;scope属性一&#xff0c;vue脚手架的基本使用 在…

vsftpd使用指北

vsftpd使用指北 文章目录vsftpd使用指北1.安装vsftpd2.登录3.切换本地路径下载命令&#xff1a;get用于下载单个文件&#xff1a;mget用于批量下载&#xff1a;上传命令&#xff1a;FilezillaReferencevsftpd 是“very secure FTP daemon”的缩写&#xff0c;是一个完全免费的、…

Vue组件化

1、Vue组件化开发思想 1.1、认识组件化开发 组件化也是类似的思想&#xff1a; 如果我们将一个页面中所有的处理逻辑全部放在一起&#xff0c;处理起来就会变得非常复杂&#xff0c;而且不利于后续的管理以及扩展&#xff1b;但如果&#xff0c;我们讲一个页面拆分成一个个小…

【算法竞赛学习】csoj:寒假第一场

文章目录前言新年礼物灯笼展摩天楼神抽新年大礼前言 由于本人菜鸡&#xff0c;所以大多都是使用出题人的代码和思路 如有侵权&#xff0c;麻烦联系up删帖&#xff0c;本贴仅作为笔记记录 本篇大多是在吹水&#xff0c;技术方面可以直接看代码注释&#xff0c;思路在水文中&am…

Linux下的进程通信之管道通信

目录 进程间通信的背景 为什么要进行进程间通信&#xff1f; 管道 什么是管道&#xff1f; 匿名管道 匿名管道原理 如何创建匿名管道&#xff1f; 命名管道 进程间通信的背景 进程间通信就是在不同的进程之间进行的数据的交换&#xff0c;进程间通信又称为Interproces…

Python SciPy 空间数据

SciPy 空间数据空间数据又称几何数据&#xff0c;它用来表示物体的位置、形态、大小分布等各方面的信息&#xff0c;比如坐标上的点。SciPy 通过 scipy.spatial 模块处理空间数据&#xff0c;比如判断一个点是否在边界内、计算给定点周围距离最近点以及给定距离内的所有点。三角…

【学Vue就跟玩一样】组件的自定义事件和全局事件总线

一&#xff0c;自定义事件1.自定义事件是什么自定义事件一种组件间通信的方式&#xff0c;适用于 子组件 ——> 父组件传输数据等2.要在什么地方使用若App是父组件&#xff0c;School是子组件&#xff0c;School想给App传数据&#xff0c;那么就要在App中给School绑定自定义…

使用k8s实现灰度发布,金丝雀,蓝绿发布

介绍#Ingress-Nginx 是一个K8S ingress工具&#xff0c;支持配置 Ingress Annotations 来实现不同场景下的灰度发布和测试。 Nginx Annotations 支持以下 4 种 Canary 规则&#xff1a;nginx.ingress.kubernetes.io/canary-by-header&#xff1a;基于 Request Header 的流量切分…

ThreadLocal底层原理

ThreadLocal底层原理一&#xff1a;什么是ThreadLocal二&#xff1a;理解ThreadLocal中的内存泄漏问题三&#xff1a;ThreadLocalMap中的Hash冲突处理四&#xff1a;可以被继承的ThreadLocal——InheritableThreadLocal一&#xff1a;什么是ThreadLocal ThreadLocal是一个创建…

【电脑故障】PIN无效,显示无用户配置文件

【电脑故障】PIN无效。显示无用户配置文件1. 电脑情况&#xff1a;2. 解决方法&#xff08;个人&#xff09;&#xff1a;2.1 解决登录问题2.2 解决PIN可用设置1. 电脑情况&#xff1a; 电脑品牌&#xff1a;联想小新pro13操作系统&#xff1a;win10 2. 解决方法&#xff08;…

C++初阶--stack和queue

目录 stack介绍 stack的使用 stack的模拟实现 queue的介绍 queue的使用 queue的模拟实现 deque priority_queue priority_queue的使用 仿函数 priority_queue的模拟实现 stack介绍&#xff1a; stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环…