使用 PDFBox 将 FormXobject 内容从资源添加到内容流?

2024-04-13

我的 page1->Resource -> Xobjects->Fm0、Fm1、Fm2 下有 FormXobject。

所以它不是直接内容流,在内容->内容流下不可用。所以我想将 Fm0->Contentstream 的内容流移动到 page1->contents->contentstream。

当我们像这样移动内容流时,我们必须同时将 Fm0 相关资源传输或复制到页面级资源。

1.内容流需要复制到页面级别的内容下。

2.色彩空间对象需要复制到page1->Resource->Colorspace下。

3.ExtGState对象需要复制到page1->Resource->ExtGState下。

4.properties需要复制到page1->Resource下(这里需要完全创建)

我尝试了一些代码

private static PDDocument parseFormXobject(PDDocument document, Integer pg_ind) throws IOException {
List<Object> tokens1 = (List<Object>) (getTokens(document, pg_ind)).get(pg_ind);
PDStream newContents = new PDStream(document);
OutputStream out = newContents.createOutputStream(COSName.FLATE_DECODE);
ContentStreamWriter writer = new ContentStreamWriter(out);

PDPage pageinner = document.getPage(pg_ind);
PDResources resources = pageinner.getResources();
PDResources new_resources = new PDResources();
new_resources = resources;

COSDictionary fntdict = new COSDictionary();
COSDictionary imgdict = new COSDictionary();
COSDictionary extgsdict = new COSDictionary();
COSDictionary colordict = new COSDictionary();
COSDictionary pattern = new COSDictionary();
int img_count = 0;
for (COSName xObjectName : resources.getXObjectNames()) {
    PDXObject xObject = resources.getXObject(xObjectName);
    if (xObject instanceof PDFormXObject
        && tokens1.toString().contains(xObjectName.toString()) ) {
        PDFStreamParser parser = new PDFStreamParser(((PDFormXObject) xObject).getContentStream());
        parser.parse();
        List<Object>  tokens3 = parser.getTokens();
        int ind =0;
        //isTextContains will check is there any Tj operators or there or not
        if (isTextContains(tokens3)){
            for (COSName colorname :((PDFormXObject) xObject).getResources().getColorSpaceNames())
            {
                COSName new_name = COSName.getPDFName(colorname.getName());
                PDColorSpace pdcolor = ((PDFormXObject) xObject).getResources().getColorSpace(colorname);
                colordict.setItem(new_name,pdcolor);
            }
            for (COSName fontName :((PDFormXObject) xObject).getResources().getFontNames() )
            {
                COSName new_name = COSName.getPDFName(fontName.getName());
                PDFont font =((PDFormXObject) xObject).getResources().getFont(fontName);
                font.getCOSObject().setItem(COSName.NAME, new_name);
                fntdict.setItem(new_name,font);
            }
            for (COSName ExtGSName :((PDFormXObject) xObject).getResources().getExtGStateNames() )
            {
                COSName new_name = COSName.getPDFName(ExtGSName.getName());
                PDExtendedGraphicsState ExtGState =((PDFormXObject) xObject).getResources().getExtGState(ExtGSName);
                ExtGState.getCOSObject().setItem(COSName.NAME, new_name);
                extgsdict.setItem(new_name,ExtGState);
            }
            imgdict.setItem(xObjectName, xObject);
            for (COSName Imgname :((PDFormXObject) xObject).getResources().getXObjectNames() )
            {
                COSName new_name = COSName.getPDFName(Imgname.getName());
                xObject.getCOSObject().setItem(COSName.NAME, new_name);
                PDXObject img =((PDFormXObject) xObject).getResources().getXObject(Imgname);
                imgdict.setItem(new_name, img);
            }
            for (COSName paternname :((PDFormXObject) xObject).getResources().getPatternNames() )
            {
                COSName new_name = COSName.getPDFName(paternname.getName());
                PDAbstractPattern pat = ((PDFormXObject) xObject).getResources().getPattern(paternname);
                pat.getCOSObject().setItem(COSName.NAME, new_name);
                pattern.setItem(new_name,pat);
            }
            for (int k=0; k< tokens1.size(); k++) {
                if ( ((tokens1.get(k) instanceof Operator) && ((Operator)tokens1.get(k)).getName().toString().equals("Do"))
                        && ((COSName)tokens1.get(k-1)).getName().toString().equals(xObjectName.getName().toString()) ) {
                    tokens1.remove(k-1);
                    tokens1.remove(k-1);
                    tokens1.add(k-1, Operator.getOperator("q"));
                    if(((PDFormXObject) xObject).getMatrix() != null) {
                        tokens1.add(k, new COSFloat(((PDFormXObject) xObject).getMatrix().getScaleX()));
                        tokens1.add(k + 1, new COSFloat(((PDFormXObject) xObject).getMatrix().getShearY()));
                        tokens1.add(k + 2, new COSFloat(((PDFormXObject) xObject).getMatrix().getShearX()));
                        tokens1.add(k + 3, new COSFloat(((PDFormXObject) xObject).getMatrix().getScaleY()));
                        tokens1.add(k + 4, new COSFloat(((PDFormXObject) xObject).getMatrix().getTranslateX()));
                        tokens1.add(k + 5, new COSFloat(((PDFormXObject) xObject).getMatrix().getTranslateY()));
                        tokens1.add(k + 6, Operator.getOperator("cm"));
                        tokens1.add(k+7, Operator.getOperator("Q"));
                        ind =k+7;
                    }else{
                        tokens1.add(k, Operator.getOperator("Q"));
                        ind =k;
                    }
                    break;
                }
            }

            for (int k=0; k< tokens3.size(); k++) {
                if ( (tokens3.size() > k+1) && (tokens3.get(k+1) instanceof Operator) && (((Operator)tokens3.get(k+1)).getName().toString().equals("Do")
                        || ((Operator)tokens3.get(k+1)).getName().toString().equals("gs")
                        || ((Operator)tokens3.get(k+1)).getName().toString().equals("cs")
                        || ((Operator)tokens3.get(k+1)).getName().toString().equals("CS")) ) {
                    COSName new_name = COSName.getPDFName( ((COSName) tokens3.get(k)).getName() );
                    tokens1.add(ind+k, new_name );
                }else if ( (tokens3.size() > k+2) && (tokens3.get(k+2) instanceof Operator)
                        && ((Operator)tokens3.get(k+2)).getName().toString().equals("Tf") ) {
                    COSName new_name = COSName.getPDFName( ((COSName) tokens3.get(k)).getName() );
                    tokens1.add(ind+k, new_name );
                }
                else
                    tokens1.add(ind+k,tokens3.get(k));
            }


            img_count +=1;
        }else {
            imgdict.setItem(xObjectName, xObject);
            img_count +=1;
        }
    }else
        imgdict.setItem(xObjectName, xObject);
}
for (COSName fontName :new_resources.getFontNames() )
{
    PDFont font =new_resources.getFont(fontName);
    fntdict.setItem(fontName,font);
}
for (COSName ExtGSName :new_resources.getExtGStateNames() )
{
    PDExtendedGraphicsState extg =new_resources.getExtGState(ExtGSName);
    extgsdict.setItem(ExtGSName,extg);
}
for (COSName colorname :new_resources.getColorSpaceNames() )
{
    PDColorSpace color =new_resources.getColorSpace(colorname);
    colordict.setItem(colorname,color);
}
for (COSName patern :new_resources.getPatternNames() )
{
    PDAbstractPattern pat =new_resources.getPattern(patern);
    pattern.setItem(patern,pat);
}

resources.getCOSObject().setItem(COSName.EXT_G_STATE,extgsdict);
resources.getCOSObject().setItem(COSName.FONT,fntdict);
resources.getCOSObject().setItem(COSName.XOBJECT,imgdict);
resources.getCOSObject().setItem(COSName.COLORSPACE, colordict);
resources.getCOSObject().setItem(COSName.PATTERN, pattern);

writer.writeTokens(tokens1);
out.close();
document.getPage(pg_ind).setContents(newContents);
document.getPage(pg_ind).setResources(resources);
return document;
}

private static JSONObject getTokens(PDDocument oldDocument, Integer pageIndex) throws IOException {
    // TODO Auto- it will return the tokens of pdf
    JSONObject oldDocumentTokens = new JSONObject();
    PDPage pg = oldDocument.getPage(pageIndex);
    PDFStreamParser parser = new PDFStreamParser(pg);
    parser.parse();
    List<Object> tokens = PDFUtils.removeTokens(parser.getTokens());
    oldDocumentTokens.put(pageIndex, tokens);
    return oldDocumentTokens;
}

private static boolean isTextContains(List<Object> tokens3) {
    for (int k=0; k< tokens3.size(); k++) {
        if (tokens3.get(k) instanceof Operator) {
            Operator op = (Operator) tokens3.get(k);
            if(op.getName().equals("BT"))
                return true;
        }
    }
    return false;
}

但我无法获得精确的页面图形。我正在失去一些东西。

输入pdf https://drive.google.com/file/d/1lT4pwGAk4I6vaekTHhWnx_hngcKCm0DK/view?usp=sharing

输出pdf https://drive.google.com/file/d/1RikX4E5WRN6fq_0Cv4PBYaMabJuHONq9/view?usp=sharing


有很多问题,有些是细节问题,有些是概念问题。

包装在保存图形状态/恢复图形状态信封中

当您绘制 XObject 时,该 XObject 中的图形状态更改不会更改当前的图形状态。为了确保在将 XObject 指令复制到页面内容流后仍然如此,您必须将该块包装到保存图形状态/恢复图形状态信封中(q ... Q)。您可以通过添加这两行来做到这一点

tokens1.add(ind++, Operator.getOperator("q"));
tokens1.add(ind, Operator.getOperator("Q"));

就在指令复制循环之前

for (int k=0; k< tokens3.size(); k++) {
    ...
}

坐标系

您假设 XObject 中的坐标系等于页面的坐标系。不一定。 XObjects 可能有一个Matrix表示要应用的转换的条目。

边界框

您不限制 XObject 指令绘制的区域。但 XObjects 有一个BBox条目表示要将输出剪辑到的框。

可选内容

XObjects 也可能有一个OC表示其可选内容成员身份的条目。这样的成员资格需要转换为等效的可选内容标签。

标记内容,结构树

XObjects 还可以通过它们的结构引用结构父树父结构体 or 结构父级入口。为了保持文档的结构完整性,您可能必须大量更新结构树。

Grouping

XObjects 可能包含Group表明其内容应被视为一个组的条目。特别是在透明度组的情况下,这会导致透明度相关功能的行为与复制到页面内容中的相同指令不同。

除非您完全分析以一定透明度绘制的每一位内容的效果,并根据具体情况重写绘制它的指令,否则将指令从 XObject 复制到页面内容流将导致显示内容存在显着差异。

Usage

您的代码假定 XObject 在页面内容流中仅使用一次。情况不一定如此,也可以更频繁地使用或根本不使用。


参考

在评论中您要求提供参考资料。实际上,这一切都在 PDF 规范 ISO 32000 中,已经在公开的 ISO 32000-1 中:

8.10 形成 XObject

A 形成XObject是一个 PDF 内容流,它是任何图形对象序列(包括路径对象、文本对象和采样图像)的独立描述。表单 XObject 可以多次绘制(在多个页面上或在同一页面上的多个位置),并且每次都会产生相同的结果,仅受调用时的图形状态的影响。

因此,给定页面上的任意数量的使用都是可能的

当。。。的时候Do运算符应用于表单 XObject,符合要求的阅读器应执行以下任务:

a) 保存当前图形状态,就像调用q运算符(参见 8.4.4,“图形状态运算符”)

b) 连接来自表单字典的矩阵Matrix输入当前变换矩阵 (CTM)

c) 根据表格词典进行剪辑BBox entry

d) 绘制表单内容流中指定的图形对象

e) 恢复保存的图形状态,就像调用Q运算符(参见 8.4.4,“图形状态运算符”)

因此,当复制到页面内容流时,您应该等效地使用q/Q信封并尊重Matrix and BBox条目。

8.11.3.3 XObject 和注释中的可选内容

除了内容流中标记的内容之外,表单 XObject 和图像 XObject(请参阅 8.8,“外部对象”)和注释(请参阅 12.5,“注释”)可能包含OC条目,它应该是可选的内容组或可选的内容成员资格字典。

表单或图像 XObject 的可见性应由组的状态或由成员资格字典结合其引用的组的状态确定。P (or VE)条目,以及调用 XObject 的上下文中的当前可见性状态(即对象在内容流中是否可见)Do发生操作)。

因此,在复制到页面内容时,请尊重此可选内容信息。

11.6.6 透明组 XObject

透明度组在 PDF 中表示为一种特殊类型的组 XObject(请参阅“组 XObject”),称为透明组 XObject。群 XObject 又是形式 XObject 的一种类型,通过存在Group其表单词典中的条目(请参阅“表单词典”)。此条目的值是子公司组属性字典定义组的属性。词典内容的格式和含义由词典的内容决定。群体亚型,由字典指定S入口。透明度组的条目(子类型透明度)如表147所示。

...

Annex L

因此,从透明度组复制可能会显着改变外观。

14.7.4.3 PDF 对象作为内容项

当结构元素的内容包括与页面关联但不直接包含在页面内容流中的整个 PDF 对象(例如 XObject 或注释)时,应在结构元素的内容中标识该对象。K由一个条目对象引用字典(见表325)。

...

14.7.4.4 从内容项中查找结构元素

...

为了定位相关的父树条目,树中表示的每个对象或内容流应包含一个特殊的字典条目,父结构体 or 结构父级(见表326)。根据内容项的类型,该条目可能出现在包含标记内容序列的页面的页面对象中、表单或图像 XObject 的流字典中、注释字典中或任何其他类型的对象字典中。作为内容项包含在结构元素中。

这一章以及同一章中的更多信息应该清楚地表明,从 XObject 复制到页面内容后的结构信息必须进行彻底检查。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 PDFBox 将 FormXobject 内容从资源添加到内容流? 的相关文章

随机推荐

  • 猫鼬和浮点值

    我的纬度和经度数字正在转换为字符串 我的部分整数仍然是 Number 的正确数据类型 如何设置模型 以便我可以将纬度和经度恢复为浮点而不是字符串 我将 latLng 数据存储在我的数据库中 现在我将纬度和经度的数据类型设置为 数字 当我检查
  • System.Web.Services.Protocols.SoapException:服务器无法识别 HTTP 标头 SOAPAction 的值:

    当我尝试调用 Web 服务上的方法时出现异常 System Web Services Protocols SoapException Server did not recognize the value of HTTP Header SOA
  • 使固定页脚与固定页眉一起使用

    我有代码让页脚始终保持在底部 即使内容高度小于窗口 http jsfiddle net 7SZ56 1 http jsfiddle net 7SZ56 1
  • 在 C 语言中,为什么数组的地址等于它的值?

    在下面的代码中 指针值和指针地址与预期不同 但数组值和地址则不然 怎么会这样 Output my array 0022FF00 my array 0022FF00 pointer to array 0022FF00 pointer to a
  • PostgreSQL - 获取物化视图列元数据

    这类似于物化视图的列数据类型 https stackoverflow com questions 31119260 column data types for materialized views但我需要更多数据 不仅仅是数据类型 我希望对
  • MonologBu​​ndle FingerCrossedHandler:如何配置exclusive_404s

    我正在开发 Symfony 2 8 6 应用程序 我尝试按照描述配置我的 Monologhere http symfony com doc current cookbook logging monolog regex based exclu
  • 为什么Java中的Set数据结构内部使用Map?

    我想知道为什么HashSet http www docjar com html api java util HashSet java html uses HashMap TreeSet uses TreeMap and LinkedHash
  • 使用 awk 计算行的平均值

    我一直在编写一个脚本 通过读取 txt 文件的输入来计算行的平均值 示例文本输入文件输入 txt 157361 155687 156158 156830 149610 151824 152353 152027 159195 158490 1
  • ASP.Net双击问题

    我的 ASP net 页面有一个小问题 如果用户双击 提交 按钮 它将写入数据库两次 即在图像按钮上执行两次 onclick 方法 如何才能使用户单击图像按钮时仅禁用图像按钮 我试过了
  • NSPredicate 过滤 Realm 和 block 的区别

    我想知道 Realm 的查询性能 鉴于此代码 let result1 realm objects Person self filter age lt 30 AND AND let result2 realm objects Person s
  • 如何使用正则表达式匹配括号内的文本?

    我有以下模式 COMPANY 277 9887 ASP 277 9887 INC 我希望最终的输出是 公司 ASP INC 目前我有以下代码 它不断返回原始模式 我假设因为该组都落在第一个 和最后一个 之间 Pattern p Patter
  • C# 类 Java 的内联扩展?

    我会在 Google MSDN 上查找此内容 但我不知道它叫什么 所以我在这里询问 在 Java 中 我似乎记得你可以做这样非常酷的事情 例如 Class MyClass int number MyClass void setNumber
  • XCode 机器人错误:“内部时间序列后错误”

    我已经设置了新的 OSX Mavericks 服务器来通过机器人运行我的 iOS 项目的测试 虽然构建和运行测试以及一切都顺利进行 但实际的集成被报告为失败 在机器人日志文件末尾 以下错误多次出现 https Request XCBotSe
  • 还有其他访问相机的替代方法吗?

    除了使用 ActionScript 3 之外 还有其他方法来访问相机吗 import flash media Camera videoInstance attachCamera cameraInstance 或者我应该使用任何 API 吗
  • VBA 控件集合(数组?)

    在寻找一种在用户表单上模拟可填充网格的方法时 我遇到了这在 Excel 先生身上 https www mrexcel com board threads datagrid on vba userform 840043 site Dim Gr
  • 解决 CouchDB 中已删除文档的复制冲突

    官方文档推荐的解决复制冲突的方式是 使用文档阅读冲突的修订版本 conflicts字段 例如通过视图 获取列出的所有修订的文档 执行特定于应用程序的合并 删除不需要的修订 当我想合并的时候问题就来了deleted文件 他们没有出现在 con
  • Angular 6 如何获取两个位置 AGM 之间的距离

    I get 方向使用此参考在两点之间https www npmjs com package agm direction https www npmjs com package agm direction现在我想获取 计算distance在两
  • C# 和不同国家的当前当地时间

    C 提供了一种获取当前日期的方法DateTime Now 但问题是我的服务器在美国 当我使用时我得到美国时间DateTime Now 由于我的用户来自世界各地 有什么方法可以在 C 中获取特定国家 地区的当前当地时间吗 我有每个用户的区域设
  • 设置为 html 文档的背景

    有没有办法让我制作一个 HTML5 画布作为我正在制作的网页的背景 并将所有元素放在它上面 所以它的作用就像 我尝试使用 z index 执行此操作并将元素定位在顶部 但随后它们是可单击或可聚焦的 我需要它们仍然正常工作 但画布也只能在背景
  • 使用 PDFBox 将 FormXobject 内容从资源添加到内容流?

    我的 page1 gt Resource gt Xobjects gt Fm0 Fm1 Fm2 下有 FormXobject 所以它不是直接内容流 在内容 gt 内容流下不可用 所以我想将 Fm0 gt Contentstream 的内容流