Search in sources :

Example 11 with PDPageContentStream

use of com.tom_roush.pdfbox.pdmodel.PDPageContentStream in project PdfBox-Android by TomRoush.

the class PDFCloneUtilityTest method testClonePDFWithCosArrayStream.

/**
 * original (minimal) test from PDFBOX-2052.
 *
 * @throws IOException
 */
public void testClonePDFWithCosArrayStream() throws IOException {
    PDDocument srcDoc = new PDDocument();
    PDDocument dstDoc = new PDDocument();
    PDPage pdPage = new PDPage();
    srcDoc.addPage(pdPage);
    new PDPageContentStream(srcDoc, pdPage, AppendMode.APPEND, true).close();
    new PDPageContentStream(srcDoc, pdPage, AppendMode.APPEND, true).close();
    new PDFCloneUtility(dstDoc).cloneForNewDocument(pdPage.getCOSObject());
    srcDoc.close();
    dstDoc.close();
}
Also used : PDPage(com.tom_roush.pdfbox.pdmodel.PDPage) PDDocument(com.tom_roush.pdfbox.pdmodel.PDDocument) PDPageContentStream(com.tom_roush.pdfbox.pdmodel.PDPageContentStream)

Example 12 with PDPageContentStream

use of com.tom_roush.pdfbox.pdmodel.PDPageContentStream in project PdfBox-Android by TomRoush.

the class PDAcroForm method flatten.

/**
 * This will flatten the specified form fields.
 *
 * <p>Flattening a form field will take the current appearance and make that part
 * of the pages content stream. All form fields and annotations associated are removed.</p>
 *
 * <p>Invisible and hidden fields will be skipped and will not become part of the
 * page content stream</p>
 *
 * @param fields
 * @param refreshAppearances if set to true the appearances for the form field widgets will be updated
 * @throws IOException
 */
public void flatten(List<PDField> fields, boolean refreshAppearances) throws IOException {
    // Nothing to flatten if there are no fields provided
    if (fields.isEmpty()) {
        return;
    }
    if (!refreshAppearances && getNeedAppearances()) {
        Log.w("PdfBox-Android", "acroForm.getNeedAppearances() returns true, " + "visual field appearances may not have been set");
        Log.w("PdfBox-Android", "call acroForm.refreshAppearances() or " + "use the flatten() method with refreshAppearances parameter");
    }
    // from the XFA content into a static PDF.
    if (xfaIsDynamic()) {
        Log.w("PdfBox-Android", "Flatten for a dynamix XFA form is not supported");
        return;
    }
    // refresh the appearances if set
    if (refreshAppearances) {
        refreshAppearances(fields);
    }
    // the content stream to write to
    PDPageContentStream contentStream;
    // get the widgets per page
    Map<COSDictionary, Set<COSDictionary>> pagesWidgetsMap = buildPagesWidgetsMap(fields);
    // preserve all non widget annotations
    for (PDPage page : document.getPages()) {
        Set<COSDictionary> widgetsForPageMap = pagesWidgetsMap.get(page.getCOSObject());
        // indicates if the original content stream
        // has been wrapped in a q...Q pair.
        boolean isContentStreamWrapped = false;
        List<PDAnnotation> annotations = new ArrayList<PDAnnotation>();
        for (PDAnnotation annotation : page.getAnnotations()) {
            if (widgetsForPageMap != null && !widgetsForPageMap.contains(annotation.getCOSObject())) {
                annotations.add(annotation);
            } else if (!annotation.isInvisible() && !annotation.isHidden() && annotation.getNormalAppearanceStream() != null && annotation.getNormalAppearanceStream().getBBox() != null) {
                contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, true, !isContentStreamWrapped);
                isContentStreamWrapped = true;
                PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream();
                PDFormXObject fieldObject = new PDFormXObject(appearanceStream.getCOSObject());
                contentStream.saveGraphicsState();
                // translate the appearance stream to the widget location if there is
                // not already a transformation in place
                boolean needsTranslation = resolveNeedsTranslation(appearanceStream);
                // scale the appearance stream - mainly needed for images
                // in buttons and signatures
                boolean needsScaling = resolveNeedsScaling(annotation, page.getRotation());
                Matrix transformationMatrix = new Matrix();
                boolean transformed = false;
                if (needsTranslation) {
                    transformationMatrix.translate(annotation.getRectangle().getLowerLeftX(), annotation.getRectangle().getLowerLeftY());
                    transformed = true;
                }
                // PDFBOX-4693: field could have a rotation matrix
                Matrix m = appearanceStream.getMatrix();
                int angle = (int) Math.round(Math.toDegrees(Math.atan2(m.getShearY(), m.getScaleY())));
                int rotation = (angle + 360) % 360;
                if (needsScaling) {
                    PDRectangle bbox = appearanceStream.getBBox();
                    PDRectangle fieldRect = annotation.getRectangle();
                    float xScale;
                    float yScale;
                    if (rotation == 90 || rotation == 270) {
                        xScale = fieldRect.getWidth() / bbox.getHeight();
                        yScale = fieldRect.getHeight() / bbox.getWidth();
                    } else {
                        xScale = fieldRect.getWidth() / bbox.getWidth();
                        yScale = fieldRect.getHeight() / bbox.getHeight();
                    }
                    Matrix scalingMatrix = Matrix.getScaleInstance(xScale, yScale);
                    transformationMatrix.concatenate(scalingMatrix);
                    transformed = true;
                }
                if (transformed) {
                    contentStream.transform(transformationMatrix);
                }
                contentStream.drawForm(fieldObject);
                contentStream.restoreGraphicsState();
                contentStream.close();
            }
        }
        page.setAnnotations(annotations);
    }
    // remove the fields
    removeFields(fields);
    // remove XFA for hybrid forms
    dictionary.removeItem(COSName.XFA);
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) COSDictionary(com.tom_roush.pdfbox.cos.COSDictionary) PDPage(com.tom_roush.pdfbox.pdmodel.PDPage) PDAppearanceStream(com.tom_roush.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream) PDAnnotation(com.tom_roush.pdfbox.pdmodel.interactive.annotation.PDAnnotation) ArrayList(java.util.ArrayList) COSArrayList(com.tom_roush.pdfbox.pdmodel.common.COSArrayList) Matrix(com.tom_roush.pdfbox.util.Matrix) PDFormXObject(com.tom_roush.pdfbox.pdmodel.graphics.form.PDFormXObject) PDPageContentStream(com.tom_roush.pdfbox.pdmodel.PDPageContentStream) PDRectangle(com.tom_roush.pdfbox.pdmodel.common.PDRectangle)

Example 13 with PDPageContentStream

use of com.tom_roush.pdfbox.pdmodel.PDPageContentStream in project PdfBox-Android by TomRoush.

the class AppearanceGeneratorHelper method insertGeneratedAppearance.

/**
 * Generate and insert text content and clipping around it.
 */
private void insertGeneratedAppearance(PDAnnotationWidget widget, PDAppearanceStream appearanceStream, OutputStream output) throws IOException {
    PDPageContentStream contents = new PDPageContentStream(field.getAcroForm().getDocument(), appearanceStream, output);
    PDRectangle bbox = resolveBoundingBox(widget, appearanceStream);
    // Acrobat calculates the left and right padding dependent on the offset of the border edge
    // This calculation works for forms having been generated by Acrobat.
    // The minimum distance is always 1f even if there is no rectangle being drawn around.
    float borderWidth = 0;
    if (widget.getBorderStyle() != null) {
        borderWidth = widget.getBorderStyle().getWidth();
    }
    PDRectangle clipRect = applyPadding(bbox, Math.max(1f, borderWidth));
    PDRectangle contentRect = applyPadding(clipRect, Math.max(1f, borderWidth));
    contents.saveGraphicsState();
    // Acrobat always adds a clipping path
    contents.addRect(clipRect.getLowerLeftX(), clipRect.getLowerLeftY(), clipRect.getWidth(), clipRect.getHeight());
    contents.clip();
    // get the font
    PDFont font = defaultAppearance.getFont();
    if (font == null) {
        throw new IllegalArgumentException("font is null, check whether /DA entry is incomplete or incorrect");
    }
    if (font.getName().contains("+")) {
        Log.w("PdfBox-Android", "Font '" + defaultAppearance.getFontName().getName() + "' of field '" + field.getFullyQualifiedName() + "' contains subsetted font '" + font.getName() + "'");
        Log.w("PdfBox-Android", "This may bring trouble with PDField.setValue(), PDAcroForm.flatten() or " + "PDAcroForm.refreshAppearances()");
        Log.w("PdfBox-Android", "You should replace this font with a non-subsetted font:");
        Log.w("PdfBox-Android", "PDFont font = PDType0Font.load(doc, new FileInputStream(fontfile), false);");
        Log.w("PdfBox-Android", "acroForm.getDefaultResources().put(COSName.getPDFName(\"" + defaultAppearance.getFontName().getName() + "\", font);");
    }
    // calculate the fontSize (because 0 = autosize)
    float fontSize = defaultAppearance.getFontSize();
    if (fontSize == 0) {
        fontSize = calculateFontSize(font, contentRect);
    }
    // options
    if (field instanceof PDListBox) {
        insertGeneratedListboxSelectionHighlight(contents, appearanceStream, font, fontSize);
    }
    // start the text output
    contents.beginText();
    // write font and color from the /DA string, with the calculated font size
    defaultAppearance.writeTo(contents, fontSize);
    // calculate the y-position of the baseline
    float y;
    // calculate font metrics at font size
    float fontScaleY = fontSize / FONTSCALE;
    float fontBoundingBoxAtSize = font.getBoundingBox().getHeight() * fontScaleY;
    float fontCapAtSize = font.getFontDescriptor().getCapHeight() * fontScaleY;
    float fontDescentAtSize = font.getFontDescriptor().getDescent() * fontScaleY;
    if (field instanceof PDTextField && ((PDTextField) field).isMultiline()) {
        y = contentRect.getUpperRightY() - fontBoundingBoxAtSize;
    } else {
        // Adobe shows the text 'shiftet up' in case the caps don't fit into the clipping area
        if (fontCapAtSize > clipRect.getHeight()) {
            y = clipRect.getLowerLeftY() + -fontDescentAtSize;
        } else {
            // calculate the position based on the content rectangle
            y = clipRect.getLowerLeftY() + (clipRect.getHeight() - fontCapAtSize) / 2;
            // check to ensure that ascents and descents fit
            if (y - clipRect.getLowerLeftY() < -fontDescentAtSize) {
                float fontDescentBased = -fontDescentAtSize + contentRect.getLowerLeftY();
                float fontCapBased = contentRect.getHeight() - contentRect.getLowerLeftY() - fontCapAtSize;
                y = Math.min(fontDescentBased, Math.max(y, fontCapBased));
            }
        }
    }
    // show the text
    float x = contentRect.getLowerLeftX();
    // chars
    if (shallComb()) {
        insertGeneratedCombAppearance(contents, appearanceStream, font, fontSize);
    } else if (field instanceof PDListBox) {
        insertGeneratedListboxAppearance(contents, appearanceStream, contentRect, font, fontSize);
    } else {
        PlainText textContent = new PlainText(value);
        AppearanceStyle appearanceStyle = new AppearanceStyle();
        appearanceStyle.setFont(font);
        appearanceStyle.setFontSize(fontSize);
        // Adobe Acrobat uses the font's bounding box for the leading between the lines
        appearanceStyle.setLeading(font.getBoundingBox().getHeight() * fontScaleY);
        PlainTextFormatter formatter = new PlainTextFormatter.Builder(contents).style(appearanceStyle).text(textContent).width(contentRect.getWidth()).wrapLines(isMultiLine()).initialOffset(x, y).textAlign(field.getQ()).build();
        formatter.format();
    }
    contents.endText();
    contents.restoreGraphicsState();
    contents.close();
}
Also used : PDFont(com.tom_roush.pdfbox.pdmodel.font.PDFont) PDPageContentStream(com.tom_roush.pdfbox.pdmodel.PDPageContentStream) PDRectangle(com.tom_roush.pdfbox.pdmodel.common.PDRectangle)

Example 14 with PDPageContentStream

use of com.tom_roush.pdfbox.pdmodel.PDPageContentStream in project PdfBox-Android by TomRoush.

the class ValidateXImage method doWritePDF.

// write image twice (overlapped) in document, close document and re-read PDF
static void doWritePDF(PDDocument document, PDImageXObject ximage, File testResultsDir, String filename) throws IOException {
    File pdfFile = new File(testResultsDir, filename);
    // This part isn't really needed because this test doesn't break
    // if the mask has the wrong colorspace (PDFBOX-2057), but it is still useful
    // if something goes wrong in the future and we want to have a PDF to open.
    PDPage page = new PDPage();
    document.addPage(page);
    PDPageContentStream contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, false);
    contentStream.drawImage(ximage, 150, 300);
    contentStream.drawImage(ximage, 200, 350);
    contentStream.close();
    // check that the resource map is up-to-date
    assertEquals(1, count(document.getPage(0).getResources().getXObjectNames()));
    document.save(pdfFile);
    document.close();
    document = PDDocument.load(pdfFile, (String) null);
    assertEquals(1, count(document.getPage(0).getResources().getXObjectNames()));
    new PDFRenderer(document).renderImage(0);
    document.close();
}
Also used : PDPage(com.tom_roush.pdfbox.pdmodel.PDPage) PDPageContentStream(com.tom_roush.pdfbox.pdmodel.PDPageContentStream) File(java.io.File) PDFRenderer(com.tom_roush.pdfbox.rendering.PDFRenderer)

Example 15 with PDPageContentStream

use of com.tom_roush.pdfbox.pdmodel.PDPageContentStream in project PdfBox-Android by TomRoush.

the class AppearanceGeneratorHelper method initializeAppearanceContent.

/**
 * Initialize the content of the appearance stream.
 *
 * Get settings like border style, border width and colors to be used to draw a rectangle and background color
 * around the widget
 *
 * @param widget the field widget
 * @param appearanceStream the appearance stream to be used
 * @throws IOException in case we can't write to the appearance stream
 */
private void initializeAppearanceContent(PDAnnotationWidget widget, PDAppearanceStream appearanceStream) throws IOException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    PDPageContentStream contents = new PDPageContentStream(field.getAcroForm().getDocument(), appearanceStream, output);
    PDAppearanceCharacteristicsDictionary appearanceCharacteristics = widget.getAppearanceCharacteristics();
    // TODO: support more entries like patterns, etc.
    if (appearanceCharacteristics != null) {
        PDColor backgroundColour = appearanceCharacteristics.getBackground();
        if (backgroundColour != null) {
            contents.setNonStrokingColor(backgroundColour);
            PDRectangle bbox = resolveBoundingBox(widget, appearanceStream);
            contents.addRect(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getWidth(), bbox.getHeight());
            contents.fill();
        }
        float lineWidth = 0f;
        PDColor borderColour = appearanceCharacteristics.getBorderColour();
        if (borderColour != null) {
            contents.setStrokingColor(borderColour);
            lineWidth = 1f;
        }
        PDBorderStyleDictionary borderStyle = widget.getBorderStyle();
        if (borderStyle != null && borderStyle.getWidth() > 0) {
            lineWidth = borderStyle.getWidth();
        }
        if (lineWidth > 0 && borderColour != null) {
            if (lineWidth != 1) {
                contents.setLineWidth(lineWidth);
            }
            PDRectangle bbox = resolveBoundingBox(widget, appearanceStream);
            PDRectangle clipRect = applyPadding(bbox, Math.max(DEFAULT_PADDING, lineWidth / 2));
            contents.addRect(clipRect.getLowerLeftX(), clipRect.getLowerLeftY(), clipRect.getWidth(), clipRect.getHeight());
            contents.closeAndStroke();
        }
    }
    contents.close();
    output.close();
    writeToStream(output.toByteArray(), appearanceStream);
}
Also used : PDAppearanceCharacteristicsDictionary(com.tom_roush.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary) PDPageContentStream(com.tom_roush.pdfbox.pdmodel.PDPageContentStream) PDRectangle(com.tom_roush.pdfbox.pdmodel.common.PDRectangle) ByteArrayOutputStream(java.io.ByteArrayOutputStream) PDBorderStyleDictionary(com.tom_roush.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary) PDColor(com.tom_roush.pdfbox.pdmodel.graphics.color.PDColor)

Aggregations

PDPageContentStream (com.tom_roush.pdfbox.pdmodel.PDPageContentStream)27 PDPage (com.tom_roush.pdfbox.pdmodel.PDPage)24 PDDocument (com.tom_roush.pdfbox.pdmodel.PDDocument)22 File (java.io.File)18 PDFont (com.tom_roush.pdfbox.pdmodel.font.PDFont)8 Test (org.junit.Test)7 Bitmap (android.graphics.Bitmap)5 PDResources (com.tom_roush.pdfbox.pdmodel.PDResources)5 PDRectangle (com.tom_roush.pdfbox.pdmodel.common.PDRectangle)5 COSDictionary (com.tom_roush.pdfbox.cos.COSDictionary)4 PDFRenderer (com.tom_roush.pdfbox.rendering.PDFRenderer)4 COSArray (com.tom_roush.pdfbox.cos.COSArray)3 Matrix (com.tom_roush.pdfbox.util.Matrix)3 ByteArrayOutputStream (java.io.ByteArrayOutputStream)3 Paint (android.graphics.Paint)2 RandomAccessBuffer (com.tom_roush.pdfbox.io.RandomAccessBuffer)2 PDFTextStripper (com.tom_roush.pdfbox.text.PDFTextStripper)2 FileOutputStream (java.io.FileOutputStream)2 IOException (java.io.IOException)2 InputStream (java.io.InputStream)2