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