List of usage examples for org.apache.pdfbox.pdmodel PDDocument close
@Override public void close() throws IOException
From source file:net.yacy.cider.parser.idiom.pdfIdiom.java
License:Open Source License
@Override public Model parse(DataSource source) throws ParserException { // create an empty Model Model model = ModelFactory.createDefaultModel(); Resource resource = source.hasURI() ? model.createResource(source.getURI().toNormalform(true, true)) : model.createResource();/*from w w w . j a v a 2 s .co m*/ // open pdf document final PDDocument theDocument; final PDFParser parser; try { parser = new PDFParser(source.getStream()); parser.parse(); theDocument = parser.getPDDocument(); } catch (IOException e) { log.error(e.getMessage(), e); throw new ParserException(e.getMessage(), source.getURI()); } if (theDocument.isEncrypted()) { try { theDocument.openProtection(new StandardDecryptionMaterial("")); } catch (BadSecurityHandlerException e) { throw new ParserException("PDF Encrypted (BadSecurityHandlerException): " + e.getMessage(), source.getURI(), e); } catch (IOException e) { throw new ParserException("PDF Encrypted (IOException): " + e.getMessage(), source.getURI(), e); } catch (CryptographyException e) { throw new ParserException("PDF Encrypted (CryptographyException): " + e.getMessage(), source.getURI(), e); } final AccessPermission perm = theDocument.getCurrentAccessPermission(); if (perm == null || !perm.canExtractContent()) throw new ParserException("PDF cannot be decrypted", source.getURI()); } // get metadata final PDDocumentInformation theDocInfo = theDocument.getDocumentInformation(); String docTitle = null, docSubject = null, docAuthor = null, docKeywordStr = null; if (theDocInfo != null) { docTitle = theDocInfo.getTitle(); docSubject = theDocInfo.getSubject(); docAuthor = theDocInfo.getAuthor(); docKeywordStr = theDocInfo.getKeywords(); } if (docAuthor != null && docAuthor.length() > 0) { resource.addProperty(VCARD.FN, docAuthor); resource.addProperty(DC.creator, docAuthor); } if (docSubject != null && docSubject.length() > 0) { resource.addProperty(DC.subject, docSubject); } if (docTitle != null && docTitle.length() > 0) { resource.addProperty(DC.title, docTitle); } String[] docKeywords = null; if (docKeywordStr != null && docKeywordStr.length() > 0) { docKeywords = docKeywordStr.split(" |,"); resource.addProperty(DC.coverage, concat(docKeywords)); } // get the content ByteArrayOutputStream baos = new ByteArrayOutputStream(); Writer writer; try { writer = new OutputStreamWriter(baos, "UTF-8"); } catch (UnsupportedEncodingException e1) { writer = new OutputStreamWriter(baos); } try { final PDFTextStripper stripper = new PDFTextStripper(); stripper.writeText(theDocument, writer); theDocument.close(); writer.close(); } catch (IOException e) { if (writer != null) try { writer.close(); } catch (final Exception ex) { } throw new ParserException("PDF content reader", source.getURI(), e); } String content; try { content = new String(baos.toByteArray(), "UTF-8"); } catch (UnsupportedEncodingException e) { content = new String(baos.toByteArray()); } if (content != null && content.length() > 0) { resource.addProperty(CIDER.data_content_text, content); } return model; }
From source file:net.yacy.document.parser.pdfParser.java
License:Open Source License
@Override public Document[] parse(final AnchorURL location, final String mimeType, final String charset, final VocabularyScraper scraper, final int timezoneOffset, final InputStream source) throws Parser.Failure, InterruptedException { // check memory for parser if (!MemoryControl.request(200 * 1024 * 1024, false)) throw new Parser.Failure("Not enough Memory available for pdf parser: " + MemoryControl.available(), location);//from w w w . jav a2s . c o m // create a pdf parser PDDocument pdfDoc; //final PDFParser pdfParser; try { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); // the pdfparser is a big pain pdfDoc = PDDocument.load(source); //PDFParser pdfParser = new PDFParser(source); //pdfParser.parse(); //pdfDoc = pdfParser.getPDDocument(); } catch (final IOException e) { throw new Parser.Failure(e.getMessage(), location); } finally { Thread.currentThread().setPriority(Thread.NORM_PRIORITY); } if (pdfDoc.isEncrypted()) { try { pdfDoc.openProtection(new StandardDecryptionMaterial("")); } catch (final BadSecurityHandlerException e) { try { pdfDoc.close(); } catch (final IOException ee) { } throw new Parser.Failure("Document is encrypted (1): " + e.getMessage(), location); } catch (final IOException e) { try { pdfDoc.close(); } catch (final IOException ee) { } throw new Parser.Failure("Document is encrypted (2): " + e.getMessage(), location); } catch (final CryptographyException e) { try { pdfDoc.close(); } catch (final IOException ee) { } throw new Parser.Failure("Document is encrypted (3): " + e.getMessage(), location); } final AccessPermission perm = pdfDoc.getCurrentAccessPermission(); if (perm == null || !perm.canExtractContent()) { try { pdfDoc.close(); } catch (final IOException ee) { } throw new Parser.Failure("Document is encrypted and cannot be decrypted", location); } } // extracting some metadata PDDocumentInformation info = pdfDoc.getDocumentInformation(); String docTitle = null, docSubject = null, docAuthor = null, docPublisher = null, docKeywordStr = null; Date docDate = new Date(); if (info != null) { docTitle = info.getTitle(); docSubject = info.getSubject(); docAuthor = info.getAuthor(); docPublisher = info.getProducer(); if (docPublisher == null || docPublisher.isEmpty()) docPublisher = info.getCreator(); docKeywordStr = info.getKeywords(); try { if (info.getModificationDate() != null) docDate = info.getModificationDate().getTime(); } catch (IOException e) { } // unused: // info.getTrapped()); } info = null; if (docTitle == null || docTitle.isEmpty()) { docTitle = MultiProtocolURL.unescape(location.getFileName()); } if (docTitle == null) { docTitle = docSubject; } String[] docKeywords = null; if (docKeywordStr != null) { docKeywords = docKeywordStr.split(" |,"); } Collection<AnchorURL>[] pdflinks = null; Document[] result = null; try { // get the links pdflinks = extractPdfLinks(pdfDoc); // get the fulltext (either per document or for each page) final PDFTextStripper stripper = new PDFTextStripper("UTF-8"); if (individualPages) { // this is a hack which stores individual pages of the source pdf into individual index documents // the new documents will get a virtual link with a post argument page=X appended to the original url // collect text int pagecount = pdfDoc.getNumberOfPages(); String[] pages = new String[pagecount]; for (int page = 1; page <= pagecount; page++) { stripper.setStartPage(page); stripper.setEndPage(page); pages[page - 1] = stripper.getText(pdfDoc); //System.out.println("PAGE " + page + ": " + pages[page - 1]); } // create individual documents for each page assert pages.length == pdflinks.length : "pages.length = " + pages.length + ", pdflinks.length = " + pdflinks.length; result = new Document[Math.min(pages.length, pdflinks.length)]; String loc = location.toNormalform(true); for (int page = 0; page < result.length; page++) { result[page] = new Document( new AnchorURL(loc + (loc.indexOf('?') > 0 ? '&' : '?') + individualPagePropertyname + '=' + (page + 1)), // these are virtual new pages; we cannot combine them with '#' as that would be removed when computing the urlhash mimeType, "UTF-8", this, null, docKeywords, singleList(docTitle), docAuthor, docPublisher, null, null, 0.0f, 0.0f, pages == null || page > pages.length ? new byte[0] : UTF8.getBytes(pages[page]), pdflinks == null || page >= pdflinks.length ? null : pdflinks[page], null, null, false, docDate); } } else { // collect the whole text at once final CharBuffer writer = new CharBuffer(odtParser.MAX_DOCSIZE); byte[] contentBytes = new byte[0]; stripper.setEndPage(3); // get first 3 pages (always) writer.append(stripper.getText(pdfDoc)); contentBytes = writer.getBytes(); // remember text in case of interrupting thread if (pdfDoc.getNumberOfPages() > 3) { // spare creating/starting thread if all pages read stripper.setStartPage(4); // continue with page 4 (terminated, resulting in no text) stripper.setEndPage(Integer.MAX_VALUE); // set to default // we start the pdf parsing in a separate thread to ensure that it can be terminated final PDDocument pdfDocC = pdfDoc; final Thread t = new Thread() { @Override public void run() { Thread.currentThread().setName("pdfParser.getText:" + location); try { writer.append(stripper.getText(pdfDocC)); } catch (final Throwable e) { } } }; t.start(); t.join(3000); // pdfbox likes to forget to terminate ... (quite often) if (t.isAlive()) t.interrupt(); } contentBytes = writer.getBytes(); // get final text before closing writer Collection<AnchorURL> pdflinksCombined = new HashSet<AnchorURL>(); for (Collection<AnchorURL> pdflinksx : pdflinks) if (pdflinksx != null) pdflinksCombined.addAll(pdflinksx); result = new Document[] { new Document(location, mimeType, "UTF-8", this, null, docKeywords, singleList(docTitle), docAuthor, docPublisher, null, null, 0.0f, 0.0f, contentBytes, pdflinksCombined, null, null, false, docDate) }; } } catch (final Throwable e) { //close the writer (in finally) //throw new Parser.Failure(e.getMessage(), location); } finally { try { pdfDoc.close(); } catch (final Throwable e) { } } // clear resources in pdfbox. they say that is resolved but it's not. see: // https://issues.apache.org/jira/browse/PDFBOX-313 // https://issues.apache.org/jira/browse/PDFBOX-351 // https://issues.apache.org/jira/browse/PDFBOX-441 // the pdfbox still generates enormeous number of object allocations and don't delete these // the following Object are statically stored and never flushed: // COSFloat, COSArray, COSInteger, COSObjectKey, COSObject, COSDictionary, // COSStream, COSString, COSName, COSDocument, COSInteger[], COSNull // the great number of these objects can easily be seen in Java Visual VM // we try to get this shit out of the memory here by forced clear calls, hope the best the rubbish gets out. pdfDoc = null; clean_up_idiotic_PDFParser_font_cache_which_eats_up_tons_of_megabytes(); return result; }
From source file:neuralclassification.Classificator.java
String readText(String filepath, String name) { PDDocument pdfDocument = null; String paper = null;// w w w . j a va 2s .c o m try { pdfDocument = PDDocument.load(new File(filepath + "/" + name)); PDFTextStripper stripper = new PDFTextStripper(); paper = stripper.getText(pdfDocument); } catch (IOException e) { throw new RuntimeException(e); } finally { if (pdfDocument != null) try { pdfDocument.close(); } catch (IOException e) { throw new RuntimeException(e); } } return paper; }
From source file:neuralclassification.Trainer.java
String readText(String name) { PDDocument pdfDocument = null; String paper = null;/*ww w. j a va2 s.c om*/ try { pdfDocument = PDDocument.load(new File(filepath + "/" + name)); PDFTextStripper stripper = new PDFTextStripper(); paper = stripper.getText(pdfDocument); } catch (IOException e) { throw new RuntimeException(e); } finally { if (pdfDocument != null) try { pdfDocument.close(); } catch (IOException e) { throw new RuntimeException(e); } } return paper; }
From source file:nominas.sei.form.Principal.java
private void ordenaNominas(String rutaEntrada, String rutaSalida) { ArrayList<PaginaNomina> paginasNomina = new ArrayList<PaginaNomina>(); for (int x = 0; x < 1; x++) {//RECORREMOS EL ARREGLO CON LOS NOMBRES DE ARCHIVO try {//from w w w .ja va 2 s .c om PDDocument pd = PDDocument.load(rutaEntrada); //CARGAR EL PDF List l = pd.getDocumentCatalog().getAllPages();//NUMERO LAS PAGINAS DEL ARCHIVO Object[] obj = l.toArray();//METO EN UN OBJETO LA LISTA DE PAGINAS PARA MANIPULARLA for (int i = 0; i < l.size(); i++) { PDPage page = (PDPage) obj[i];//PAGE ES LA PAGINA 1 DE LA QUE CONSTA EL ARCHIVO PageFormat pageFormat = pd.getPageFormat(0);//PROPIEDADES DE LA PAGINA (FORMATO) Double d1 = new Double(pageFormat.getHeight());//ALTO Double d2 = new Double(pageFormat.getWidth());//ANCHO int width = d1.intValue();//ANCHO int eigth = 1024;//ALTO PDFTextStripperByArea stripper = new PDFTextStripperByArea();//COMPONENTE PARA ACCESO AL TEXTO Rectangle rect = new Rectangle(0, 0, width, eigth);//DEFNIR AREA DONDE SE BUSCARA EL TEXTO stripper.addRegion("area1", rect);//REGISTRAMOS LA REGION CON UN NOMBRE stripper.extractRegions(page);//EXTRAE TEXTO DEL AREA String contenido = new String();//CONTENIDO = A LO QUE CONTENGA EL AREA O REGION contenido = (stripper.getTextForRegion("area1")); String[] lines = contenido.split("[\\r\\n]+"); String nombre = lines[1].substring(28, lines[1].length() - 10);//Separamos el nombre PaginaNomina nomina = new PaginaNomina(page, nombre); paginasNomina.add(nomina); } Collections.sort(paginasNomina); // Create a new empty document PDDocument document = new PDDocument(); for (int i = 0; i < paginasNomina.size(); i++) { System.out.println(paginasNomina.get(i).getNombre()); document.addPage(paginasNomina.get(i).getPagina()); } // Save the newly created document document.save(rutaSalida); // finally make sure that the document is properly // closed. document.close(); pd.close();//CERRAMOS OBJETO ACROBAT } catch (Exception e) { System.out.println(e.getMessage()); } //CATCH } //FOR }
From source file:nominas.sei.NominasSEI.java
/** * @param args the command line arguments *//*from w ww . j a v a2 s .c om*/ public static void main(String[] args) { ArrayList<PaginaNomina> paginasNomina = new ArrayList<PaginaNomina>(); for (int x = 0; x < 1; x++) {//RECORREMOS EL ARREGLO CON LOS NOMBRES DE ARCHIVO String ruta = new String();//VARIABLE QUE DETERMINARA LA RUTA DEL ARCHIVO A LEER. ruta = (".\\NOMINAS.pdf"); //SE ALMACENA LA RUTA DEL ARCHIVO A LEER. try { PDDocument pd = PDDocument.load(ruta); //CARGAR EL PDF List l = pd.getDocumentCatalog().getAllPages();//NUMERO LAS PAGINAS DEL ARCHIVO Object[] obj = l.toArray();//METO EN UN OBJETO LA LISTA DE PAGINAS PARA MANIPULARLA for (int i = 0; i < l.size(); i++) { PDPage page = (PDPage) obj[i];//PAGE ES LA PAGINA 1 DE LA QUE CONSTA EL ARCHIVO PageFormat pageFormat = pd.getPageFormat(0);//PROPIEDADES DE LA PAGINA (FORMATO) Double d1 = new Double(pageFormat.getHeight());//ALTO Double d2 = new Double(pageFormat.getWidth());//ANCHO int width = d1.intValue();//ANCHO int eigth = 1024;//ALTO PDFTextStripperByArea stripper = new PDFTextStripperByArea();//COMPONENTE PARA ACCESO AL TEXTO Rectangle rect = new Rectangle(0, 0, width, eigth);//DEFNIR AREA DONDE SE BUSCARA EL TEXTO stripper.addRegion("area1", rect);//REGISTRAMOS LA REGION CON UN NOMBRE stripper.extractRegions(page);//EXTRAE TEXTO DEL AREA String contenido = new String();//CONTENIDO = A LO QUE CONTENGA EL AREA O REGION contenido = (stripper.getTextForRegion("area1")); String[] lines = contenido.split("[\\r\\n]+"); String nombre = lines[1].substring(28, lines[1].length() - 10); PaginaNomina nomina = new PaginaNomina(page, nombre); paginasNomina.add(nomina); } Collections.sort(paginasNomina); // Create a new empty document PDDocument document = new PDDocument(); for (int i = 0; i < paginasNomina.size(); i++) { System.out.println(paginasNomina.get(i).getNombre()); document.addPage(paginasNomina.get(i).getPagina()); } // Save the newly created document document.save("NominasOrdenadas.pdf"); // finally make sure that the document is properly // closed. document.close(); pd.close();//CERRAMOS OBJETO ACROBAT } catch (Exception e) { System.out.println(e.getMessage()); } //CATCH } //FOR }
From source file:noprint.NoPrint.java
/** * @param args the command line arguments * @throws IOException in case input file is can't be read or output written * @throws org.apache.pdfbox.pdmodel.encryption.BadSecurityHandlerException * @throws org.apache.pdfbox.exceptions.COSVisitorException *//* ww w.java2 s . c o m*/ public static void main(String[] args) throws IOException, BadSecurityHandlerException, COSVisitorException { String infile = "input.pdf"; String outfile = "output.pdf"; String ownerPass = ""; String userPass = ""; /** * TODO: read up what the actual difference is between * userpassword and ownerpassword. */ int keylength = 40; AccessPermission ap = new AccessPermission(); PDDocument document = null; ap.setCanAssembleDocument(true); ap.setCanExtractContent(true); ap.setCanExtractForAccessibility(true); ap.setCanFillInForm(true); ap.setCanModify(true); ap.setCanModifyAnnotations(true); ap.setCanPrintDegraded(true); ap.setCanPrint(false); // YOU CAN'T PRINT // at least not when your PDFreader adheres to DRM (some don't) // also this is trivial to remove document = PDDocument.load(infile); if (!document.isEncrypted()) { StandardProtectionPolicy spp; spp = new StandardProtectionPolicy(ownerPass, userPass, ap); spp.setEncryptionKeyLength(keylength); document.protect(spp); document.save(outfile); } if (document != null) { document.close(); } }
From source file:nz.co.testamation.core.reader.pdf.PdfContentReaderImpl.java
License:Apache License
private String getPdfText(CloseableHttpResponse response) throws IOException { PDDocument load = PDDocument.load(response.getEntity().getContent()); try {/*from w ww . j a va 2 s . c o m*/ return new PDFTextStripper().getText(load).replaceAll("\\s+", " "); } finally { load.close(); } }
From source file:ocr_pdf.OCR_PDF.java
License:GNU General Public License
/** * Get PDFBox output of airport diagram. * @param fileName name of airport diagram PDF file. * @return text representation of airport diagram. *//*from w w w . ja v a 2 s .com*/ static String getTextPDFBox(String fileName) { PDFParser parser; String parsedText = null; PDFTextStripper pdfStripper = null; PDDocument pdDoc = null; COSDocument cosDoc = null; File file = new File(fileName); if (!file.isFile()) { System.err.println("File " + fileName + " does not exist."); return null; } try { parser = new PDFParser(new FileInputStream(file)); } catch (IOException e) { System.err.println("Unable to open PDF Parser. " + e.getMessage()); return null; } try { parser.parse(); cosDoc = parser.getDocument(); pdfStripper = new PDFTextStripper(); //true doesn't work so well. pdfStripper.setSortByPosition(false); pdDoc = new PDDocument(cosDoc); parsedText = pdfStripper.getText(pdDoc); } catch (Exception e) { System.err.println("An exception occured in parsing the PDF Document." + e.getMessage()); } finally { try { if (cosDoc != null) cosDoc.close(); if (pdDoc != null) pdDoc.close(); } catch (Exception e) { e.printStackTrace(); } } return parsedText; }
From source file:openstitcher.core.PDFRenderer.java
License:Open Source License
public static void render(Design design, String location, ArrayList<LegendEntry> legend) throws IOException, COSVisitorException { PDDocument document = new PDDocument(); PDPage page = new PDPage(); document.addPage(page);/* w ww.ja v a 2s. co m*/ PDFont font = PDType1Font.HELVETICA; PDPageContentStream contentStream = new PDPageContentStream(document, page); float pageWidth = page.getMediaBox().getWidth(); float pageHeight = page.getMediaBox().getHeight(); float sideMargin = 70.0f; float capsMargin = 30.0f; // draw the document title contentStream.beginText(); contentStream.setFont(font, 24.0f); contentStream.moveTextPositionByAmount(sideMargin, pageHeight - capsMargin - 24.0f); contentStream.drawString(design.title); contentStream.endText(); // draw the document author contentStream.beginText(); contentStream.setFont(font, 12.0f); contentStream.moveTextPositionByAmount(sideMargin, pageHeight - capsMargin - (24.0f + 12.0f)); contentStream.drawString(design.author); contentStream.endText(); // draw the document license contentStream.beginText(); contentStream.setFont(font, 12.0f); contentStream.moveTextPositionByAmount(sideMargin, pageHeight - capsMargin - (24.0f + 12.0f + 12.0f)); contentStream.drawString(design.license); contentStream.endText(); // draw the physical size contentStream.beginText(); contentStream.setFont(font, 12.0f); contentStream.moveTextPositionByAmount(pageWidth - sideMargin - 100.0f, pageHeight - capsMargin - (24.0f + 12.0f + 12.0f)); contentStream.drawString(design.physicalSize); contentStream.endText(); // draw the pattern float gridWidth = pageWidth - (sideMargin * 2); float widthPerCell = gridWidth / (float) design.pattern.getPatternWidth(); float gridStartX = sideMargin; float gridStopX = sideMargin + (widthPerCell * design.pattern.getPatternWidth()); float gridStartY = pageHeight - capsMargin - (24.0f + 12.0f + 12.0f + 12.0f); float gridStopY = (pageHeight - capsMargin - (24.0f + 12.0f + 12.0f + 12.0f)) - (widthPerCell * design.pattern.getPatternHeight()); // draw the pattern: background for (int i = 0; i < design.pattern.getPatternWidth(); i++) { for (int j = 0; j < design.pattern.getPatternHeight(); j++) { Yarn cellYarn = design.pattern.getPatternCell(i, j); if (cellYarn != null) { contentStream.setNonStrokingColor(cellYarn.color); contentStream.fillRect(gridStartX + (widthPerCell * i), gridStartY - (widthPerCell * j) - widthPerCell, widthPerCell, widthPerCell); } } } // draw the pattern: grid outline contentStream.setStrokingColor(Color.black); for (int i = 0; i < design.pattern.getPatternWidth() + 1; i++) { // draw vertical lines float xCoord = gridStartX + (widthPerCell * i); if (i % 5 == 0) { contentStream.setLineWidth(2.0f); } else { contentStream.setLineWidth(1.0f); } contentStream.drawLine(xCoord, gridStartY, xCoord, gridStopY); } for (int i = 0; i < design.pattern.getPatternHeight() + 1; i++) { // draw horizontal lines float yCoord = gridStartY - (widthPerCell * i); if (i % 5 == 0) { contentStream.setLineWidth(2.0f); } else { contentStream.setLineWidth(1.0f); } contentStream.drawLine(gridStartX, yCoord, gridStopX, yCoord); } // draw the pattern: characters contentStream.setNonStrokingColor(Color.black); float centeringOffset = widthPerCell / 5.0f; for (int i = 0; i < design.pattern.getPatternWidth(); i++) { for (int j = 0; j < design.pattern.getPatternHeight(); j++) { Yarn cellYarn = design.pattern.getPatternCell(i, j); if (cellYarn != null) { int index = LegendEntry.findIndexByYarn(legend, cellYarn); if (index == -1) { throw new RuntimeException("Cell did not exist in legend."); } contentStream.beginText(); contentStream.setFont(font, widthPerCell); contentStream.moveTextPositionByAmount(gridStartX + (widthPerCell * i) + centeringOffset, gridStartY - (widthPerCell * j) - widthPerCell + centeringOffset); contentStream.drawString(legend.get(index).character); contentStream.endText(); } } } // draw the legend float legendWidth = pageWidth - (sideMargin * 2); float widthPerLegendCell = legendWidth / (float) legend.size(); float legendStartX = sideMargin; float legendStopX = pageWidth - sideMargin; float legendStartY = capsMargin + 12.0f; float legendStopY = capsMargin; float legendCellPadding = 1.0f; float exampleCellWidth = 10.0f; for (int i = 0; i < legend.size(); i++) { // draw box contentStream.setNonStrokingColor(legend.get(i).yarn.color); contentStream.fillRect(legendStartX + legendCellPadding + (i * widthPerLegendCell), legendStopY + legendCellPadding, exampleCellWidth, exampleCellWidth); // draw character contentStream.beginText(); contentStream.setNonStrokingColor(legend.get(i).fontColor); contentStream.setFont(font, 10.0f); contentStream.moveTextPositionByAmount(legendStartX + legendCellPadding + (i * widthPerLegendCell), legendStopY + legendCellPadding); contentStream.drawString(legend.get(i).character); contentStream.endText(); // draw yarn name contentStream.beginText(); contentStream.setNonStrokingColor(Color.black); contentStream.setFont(font, 10.0f); contentStream.moveTextPositionByAmount(legendStartX + legendCellPadding + exampleCellWidth + legendCellPadding + (i * widthPerLegendCell), legendStopY + legendCellPadding); contentStream.drawString(legend.get(i).yarn.name); contentStream.endText(); } contentStream.close(); document.save(location); document.close(); }