是否使用PDFBox将FormXObject内容从资源添加到内容流?

2022-04-25 00:00:00 pdf java pdfbox

我的page1下有FormXObject-&>资源-&>X对象->;FM0,FM1,FM2..

因此它不是Contents->;Contentstream下不可用的直接内容流。因此,我想将的内容流从fm0-&>内容流移动到page1-&>内容-&>内容流。

当我们像这样并行移动内容流时,我们必须将FM0相关资源转移或复制到页面级资源。

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

2.色彩空间对象需要复制到page1->;资源->;色彩空间下。

3.ExtGState对象需要复制到page1->;资源->;ExtGState下。

4.属性需要复制到page1下-&>资源(此处需要完全创建)

我尝试了一些代码

private PDDocument parseFormXobject(PDDocument document) throws IOException {
PDDocument newdocument = new PDDocument();
for (int pg_ind = 0; pg_ind < document.getNumberOfPages(); pg_ind++) {
    List<Object> tokens1 = (List<Object>) (getTokens(document)).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();
    int img_count = 0;
    for (COSName xObjectName : resources.getXObjectNames()) {
        PDXObject  xObject = resources.getXObject(xObjectName);
        if (xObject instanceof PDFormXObject) {

            PDFStreamParser parser = new PDFStreamParser(((PDFormXObject) xObject).getContentStream());
            parser.parse();
            List<Object>  tokens3 = parser.getTokens();
            int ind =0;
            System.out.println(xObjectName.getName());
                for (COSName colorname :((PDFormXObject) xObject).getResources().getColorSpaceNames())
                {
                    COSName new_name = COSName.getPDFName(colorname.getName()+"_Fm"+img_count);
                    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()+"_Fm"+img_count);
                    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()+"_Fm"+img_count);
                    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()+"_Fm"+img_count);
                    xObject.getCOSObject().setItem(COSName.NAME, new_name);
                    PDXObject img =((PDFormXObject) xObject).getResources().getXObject(Imgname);
                    imgdict.setItem(new_name, img);
                }

                    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()) ) {
                            System.out.println(tokens1.get(k).toString());
                            tokens1.remove(k-1);
                            tokens1.remove(k-1);
                            ind =k-1;
                            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")  ) ) {
                        COSName new_name = COSName.getPDFName( ((COSName) tokens3.get(k)).getName()+"_Fm"+img_count );
                        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()+"_Fm"+img_count );
                        tokens1.add(ind+k, new_name );
                    }else
                        tokens1.add(ind+k,tokens3.get(k));
                }

                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);
    }
    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);

    writer.writeTokens(tokens1);
    out.close();
    document.getPage(pg_ind).setContents(newContents);
    document.getPage(pg_ind).setMediaBox(PDFUtils.Media_box);
    document.getPage(pg_ind).setResources(resources);
    newdocument.addPage(document.getPage(pg_ind));
}
newdocument.save("D:/Testfiles/stu.pdf");
return newdocument;
}

但我无法获得准确的页面图形。我失去了一些东西。

input pdf

output pdf


解决方案

有多个问题,有些是详细问题,有些是概念问题。

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

在绘制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中的坐标系等于页面的坐标系。这并不一定。XObject可能有一个Matrix条目,表示要应用的转换。

边界框

您不会限制XObject指令所绘制的区域。但是XObject有一个bBox条目,表示要将输出裁剪到的框。

可选内容

XObject还可能有OC条目,表示它们的可选内容成员身份。此类成员身份需要转换为等效的可选内容标记。

标记内容、结构树

XObject还可以通过StructParent或StructParents条目引用结构父树。为了保持文档的结构完整性,您可能需要大量更新结构树。

分组

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

除非您完全分析以一定透明度绘制的每一位内容的效果,并逐一重写绘制它的指令,否则将指令从XObject复制到页面内容流将导致显示的内容有很大差异。

用法

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


参考资料

在您要求提供推荐人的评论中。实际上,它都在pdf规范ISO 32000中,已经在公开可用的ISO 32000-1中:

8.10表单XObject

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

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

将do运算符应用于Form XObject时,符合要求的读取器应执行以下任务:

a)保存当前图形状态,就像通过调用q运算符一样(参见8.4.4;图形状态运算符)

b)将表单词典的矩阵条目中的矩阵与当前转换矩阵(CTM)连接

c)根据表单词典的bBox条目进行剪辑

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

e)恢复保存的图形状态,就像通过调用q运算符一样(参见8.4.4;图形状态运算符

)

因此,在复制到页面内容流中时,您应该等效使用q/q信封,并遵守Matrix和bbox条目。

8.11.3.3 XObject和批注中的可选内容

除了内容流中的标记内容外,表单XObject和图像XObject(请参阅8.8;外部对象)和批注(请参阅12.5;批注和注释)还可以包含OC条目,该条目应该是可选的内容组或可选的内容成员词典。

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

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

11.6.6透明组XObject

透明度组在PDF中表示为称为透明度组XObject的特殊类型的组XObject(请参阅"Group XObject")。组XObject又是一种表单XObject,其区别在于其表单词典中存在Group条目(请参阅"表单词典")。此条目的值是定义组属性的附属组属性字典。词典内容的格式和含义应由其组子类型确定,该子类型由词典的S条目指定。透明度组(子类型透明度)的条目如表147所示。

...

附件L

因此,从透明组复制可能会显著更改外观。

14.7.4.3作为内容项目的PDF对象

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

...

14.7.4.4从内容项查找结构元素

...

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

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

相关文章