You need to enable JavaScript to run this app. 使用PDFBox将FormXObject内容并入页面内容流并迁移关联资源的实现问题
优惠活动
大模型
产品
解决方案
定价
更多

使用PDFBox将FormXObject内容并入页面内容流并迁移关联资源的实现问题

FormXObject 内容流迁移实现后出现内容丢失问题

业务场景

  • 第一页(page1)的Resource->Xobjects路径下存储了Fm0、Fm1、Fm2等多个FormXobject对象
  • 上述FormXobject的内容未直接挂载在页面的contents->contentstream节点下,因此需要将Fm0的内容流迁移至page1的contents->contentstream中
  • 迁移过程中需要将Fm0关联的所有Resource同步转移或拷贝到页面级资源目录下,结构可参考示意图

迁移要求

  • 将FormXObject的内容流拷贝至页面级Contents目录下
  • 将Colorspace对象拷贝至page1->Resource->Colorspace目录下
  • 将ExtGState对象拷贝至page1->Resource->ExtGState目录下
  • 在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;
            // 判断FormXObject内容流中是否包含文本绘制标记
            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 {
    // 提取指定页面的内容流操作Token列表
    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无法还原原有的页面图形效果,存在内容丢失的问题。已准备输入PDF样本、输出PDF样本用于效果比对。

内容的提问来源于stack exchange,提问作者fascinating coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.02 21:54:04