Java tutorial
/* * This file is part of the ZoRa project: http://www.photozora.org. * * ZoRa is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ZoRa is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZoRa; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * (c) 2009 Berthold Daum */ package com.bdaum.zoom.email.internal.job; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Display; import com.bdaum.zoom.batch.internal.IFileWatcher; import com.bdaum.zoom.cat.model.PageLayout_type; import com.bdaum.zoom.cat.model.PageLayout_typeImpl; import com.bdaum.zoom.cat.model.asset.Asset; import com.bdaum.zoom.cat.model.meta.Meta; import com.bdaum.zoom.core.Assetbox; import com.bdaum.zoom.core.Constants; import com.bdaum.zoom.core.Core; import com.bdaum.zoom.core.IVolumeManager; import com.bdaum.zoom.core.QueryField; import com.bdaum.zoom.core.db.IDbManager; import com.bdaum.zoom.core.internal.CoreActivator; import com.bdaum.zoom.email.internal.Activator; import com.bdaum.zoom.image.ImageConstants; import com.bdaum.zoom.image.ImageUtilities; import com.bdaum.zoom.image.ZImage; import com.bdaum.zoom.image.internal.ImageActivator; import com.bdaum.zoom.image.recipe.UnsharpMask; import com.bdaum.zoom.job.CustomJob; import com.bdaum.zoom.job.OperationJob; import com.bdaum.zoom.ui.internal.dialogs.PageProcessor; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.FontFactory; import com.itextpdf.text.PageSize; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; @SuppressWarnings("restriction") public class PdfJob extends CustomJob { private static final int PRINTERDPI = 300; private static final int SCREENDPI = 120; private static final float MMPERINCH = 25.4f; private static final int fontSize = 7; private static final int titleSize = 11; private static final int subtitleSize = 9; private static final int footerSize = 8; private static final Rectangle[] PAGEFORMATS = new Rectangle[] { PageSize.A0, PageSize.A1, PageSize.A2, PageSize.A3, PageSize.A4, PageSize.A5, PageSize.A6, PageSize.B0, PageSize.B1, PageSize.B2, PageSize.B3, PageSize.B4, PageSize.B5, PageSize.B6, PageSize.B7, PageSize.LETTER, PageSize.HALFLETTER, PageSize.LEGAL, PageSize._11X17 }; private List<Asset> assets; private PageLayout_typeImpl layout; private File targetFile; private final int quality; private final int jpegQuality; private float keyLine; private float fontLead; private int titlePixelSize; private int subtitlePixelSize; private int subtitleLead; private int titleTextSize; private float leftMargins; private float rightMargins; private float topMargins; private float bottomMargins; private Rectangle format; private float cellWidth; private double imageWidth; private double imageHeight; private float cellHeight; private float upperWaste; private int imagesPerPage; private int pages; private Date now; private Meta meta; private String fileName; private final int cms; private String collection; private int rows; private float verticalGap; private int seqNo = 0; private ZImage zimage; private float horizontalGap; private int titleLead; private int footerLead; private String subject; private String message; private List<File> tempFiles = new ArrayList<File>(); private final UnsharpMask unsharpMask; protected String opId = java.util.UUID.randomUUID().toString(); protected IFileWatcher fileWatcher = CoreActivator.getDefault().getFileWatchManager(); public PdfJob(List<Asset> assets, PageLayout_typeImpl layout, File targetFile, int quality, int jpegQuality, int cms, UnsharpMask unsharpMask, String collection) { super(Messages.PdfJob_Create_x); this.jpegQuality = jpegQuality; this.assets = assets; this.layout = layout; this.targetFile = targetFile; this.quality = quality; this.cms = cms; this.unsharpMask = unsharpMask; this.collection = collection; } @Override public boolean belongsTo(Object family) { return Constants.OPERATIONJOBFAMILY == family || Constants.CRITICAL == family; } @Override protected IStatus runJob(IProgressMonitor monitor) { long startTime = System.currentTimeMillis(); MultiStatus status = new MultiStatus(Activator.PLUGIN_ID, 0, Messages.PdfJob_pdf_report, null); setup(); monitor.beginTask(NLS.bind(Messages.PdfJob_creating_pdf_pages, pages), assets.size() + 1); Document document = createDocument(); writeDocument(document, status, monitor); OperationJob.signalJobEnd(startTime); if (subject != null && message != null && !monitor.isCanceled()) { IStatus sendStatus = Activator.getDefault().sendMail(null, null, null, subject, message, Collections.singletonList(targetFile.toURI().toString())); if (!sendStatus.isOK()) status.add(sendStatus); } cleanUp(); if (subject == null) Program.launch(targetFile.getAbsolutePath()); return status; } private void cleanUp() { for (File file : tempFiles) file.delete(); tempFiles.clear(); } private void setup() { format = computeFormat(layout); double minAspectRatio = Double.MAX_VALUE; double maxAspectRatio = 0; for (Asset asset : assets) { Double aspectRatio = (Double) QueryField.ASPECTRATIO.obtainFieldValue(asset); if (aspectRatio != null) { maxAspectRatio = Math.max(maxAspectRatio, aspectRatio); minAspectRatio = Math.min(minAspectRatio, aspectRatio); } } minAspectRatio = (minAspectRatio + maxAspectRatio) / 2; keyLine = layout.getKeyLine() * 0.5f; fontLead = fontSize / 3f; titlePixelSize = 0; titleLead = 0; if (!layout.getTitle().isEmpty()) titleLead = titlePixelSize = titleSize; subtitlePixelSize = 0; subtitleLead = 0; if (!layout.getSubtitle().isEmpty()) subtitleLead = subtitlePixelSize = subtitleSize; titleTextSize = titlePixelSize + titleLead + subtitlePixelSize + subtitleLead; footerLead = footerSize; int footerTextSize = footerSize + footerLead; int textHeight = 0; if (!layout.getCaption1().isEmpty()) textHeight += fontSize + fontLead; if (!layout.getCaption2().isEmpty()) textHeight += fontSize + fontLead; leftMargins = layout.getLeftMargin() / MMPERINCH * 72; rightMargins = layout.getRightMargin() / MMPERINCH * 72; horizontalGap = layout.getHorizontalGap() / MMPERINCH * 72; topMargins = layout.getTopMargin() / MMPERINCH * 72; bottomMargins = layout.getBottomMargin() / MMPERINCH * 72; verticalGap = layout.getVerticalGap() / MMPERINCH * 72; float useableWidth = format.getWidth() - leftMargins - rightMargins; cellWidth = (useableWidth + horizontalGap) / layout.getColumns(); float useableHeight = format.getHeight() - topMargins - bottomMargins - titleTextSize - footerTextSize; imageWidth = cellWidth - horizontalGap - keyLine; float maxImageHeight = useableHeight - textHeight - verticalGap - keyLine; imageHeight = imageWidth / minAspectRatio; while (imageHeight > maxImageHeight) { imageWidth *= 0.99d; imageHeight = imageWidth / minAspectRatio; } cellHeight = (float) (imageHeight + textHeight + verticalGap + keyLine); rows = (int) ((useableHeight + verticalGap) / cellHeight); float usedSpace = rows * cellHeight; upperWaste = (useableHeight - usedSpace) / 2; imagesPerPage = layout.getColumns() * rows; pages = Math.max(1, (assets.size() + imagesPerPage - 1) / imagesPerPage); now = new Date(); IDbManager dbManager = Core.getCore().getDbManager(); meta = dbManager.getMeta(true); fileName = dbManager.getFileName(); } public static Rectangle computeFormat(PageLayout_type layout) { Rectangle format = PageSize.A4; String f = layout.getFormat(); for (int i = 0; i < PageLayout_type.formatALLVALUES.length; i++) if (PageLayout_type.formatALLVALUES[i].equals(f)) { format = PAGEFORMATS[i]; break; } return (layout.getLandscape()) ? format.rotate() : format; } private Document createDocument() { Document document = new Document(); document.addCreationDate(); document.addCreator(Constants.APPLICATION_NAME); document.addAuthor(System.getProperty("user.name")); //$NON-NLS-1$ document.setPageSize(format); document.setMargins(Math.max(0, leftMargins - horizontalGap / 2), Math.max(0, rightMargins - horizontalGap / 2), topMargins, 0); document.setMarginMirroring(layout.getFacingPages()); return document; } private void writeDocument(Document document, MultiStatus status, IProgressMonitor monitor) { try (FileOutputStream out = new FileOutputStream(targetFile)) { PdfWriter.getInstance(document, out); document.open(); for (int i = 0; i < pages; i++) { printPage(document, i + 1, status, monitor); if (monitor.isCanceled()) { status.add(new Status(IStatus.WARNING, Activator.PLUGIN_ID, Messages.PdfJob_pdf_creation_cancelled)); break; } } document.close(); } catch (FileNotFoundException e) { // should not happen } catch (DocumentException e) { status.add( new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.PdfJob_internal_error_when_writing, e)); } catch (IOException e) { status.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.PdfJob_io_error_when_writing, e)); } finally { fileWatcher.stopIgnoring(opId); } } private void printPage(Document document, int pageNo, MultiStatus status, IProgressMonitor monitor) throws DocumentException, IOException { if (pageNo > 1) document.newPage(); document.setPageCount(pageNo); final Display display = Display.getDefault(); int pageItem = 0; if (!layout.getTitle().isEmpty()) { String title = PageProcessor.computeTitle(layout.getTitle(), fileName, now, assets.size(), pageNo, pages, collection, meta); Paragraph p = new Paragraph(title, FontFactory.getFont(FontFactory.HELVETICA, titleSize, Font.BOLD, BaseColor.DARK_GRAY)); p.setAlignment(Element.ALIGN_CENTER); p.setSpacingAfter(layout.getSubtitle().isEmpty() ? titleLead + upperWaste : titleLead); document.add(p); } if (!layout.getSubtitle().isEmpty()) { String subtitle = PageProcessor.computeTitle(layout.getSubtitle(), fileName, now, assets.size(), pageNo, pages, collection, meta); Paragraph p = new Paragraph(subtitle, FontFactory.getFont(FontFactory.HELVETICA, subtitleSize, Font.NORMAL, BaseColor.DARK_GRAY)); p.setAlignment(Element.ALIGN_CENTER); p.setSpacingAfter(subtitleLead + upperWaste); document.add(p); } IVolumeManager vm = Core.getCore().getVolumeManager(); PdfPTable table = new PdfPTable(layout.getColumns()); try (Assetbox box = new Assetbox(null, status, false)) { for (int i = 0; i < rows; i++) { int ni = i * layout.getColumns(); for (int j = 0; j < layout.getColumns(); j++) { int a = (pageNo - 1) * imagesPerPage + ni + j; PdfPCell cell; if (a >= assets.size() || monitor.isCanceled()) cell = new PdfPCell(); else { final int dpi = quality == Constants.SCREEN_QUALITY ? SCREENDPI : PRINTERDPI; final int pixelWidth = (int) (imageWidth * dpi / 72); final int pixelHeight = (int) (imageHeight * dpi / 72); Asset asset = assets.get(a); zimage = new ZImage(ImageUtilities.loadThumbnail(display, asset.getJpegThumbnail(), cms, SWT.IMAGE_JPEG, true), null); org.eclipse.swt.graphics.Rectangle bounds = zimage.getBounds(); URI uri = vm.findExistingFile(asset, false); if (uri != null) { boolean r = asset.getRotation() % 180 != 0; double w = r ? asset.getHeight() : asset.getWidth(); double h = r ? asset.getWidth() : asset.getHeight(); double scale = w == 0 || h == 0 ? 1d : Math.min(pixelWidth / w, pixelHeight / h); scale = (scale <= 0.5d) ? 0.5d : 1d; File file = box.obtainFile(uri); if (file != null) try { ZImage hzimage = CoreActivator.getDefault().getHighresImageLoader().loadImage( null, status, file, asset.getRotation(), asset.getFocalLengthIn35MmFilm(), null, scale, Double.MAX_VALUE, true, ImageConstants.SRGB, null, unsharpMask, null, fileWatcher, opId, null); if (hzimage != null) { zimage.dispose(); zimage = hzimage; } } catch (UnsupportedOperationException e) { // do nothing } } display.syncExec(() -> { int kl = (keyLine > 0) ? (int) Math.max(1, (keyLine * dpi / 144)) : 0; org.eclipse.swt.graphics.Rectangle ibounds = zimage.getBounds(); double factor = Math.min((double) pixelWidth / ibounds.width, (double) pixelHeight / ibounds.height); int lw = pixelWidth + 2 * kl; int lh = pixelHeight + 2 * kl; Image newImage = new Image(display, lw, lh); GC gc = new GC(newImage); try { gc.setAntialias(SWT.ON); gc.setInterpolation(SWT.HIGH); gc.setAdvanced(true); gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); gc.fillRectangle(0, 0, lw, lh); int width = (int) (ibounds.width * factor + 2 * kl); int height = (int) (ibounds.height * factor + 2 * kl); int xoff = (lw - width) / 2; int yoff = (lh - height) / 2; if (kl > 0) { gc.setBackground(display.getSystemColor(SWT.COLOR_DARK_GRAY)); gc.fillRectangle(xoff, yoff, width, height); } zimage.draw(gc, 0, 0, ibounds.width, ibounds.height, xoff + kl, yoff + kl, width - 2 * kl, height - 2 * kl, ZImage.CROPPED, pixelWidth, pixelHeight, false); } finally { gc.dispose(); zimage.dispose(); zimage = new ZImage(newImage, null); } }); bounds = zimage.getBounds(); File jpegFile = ImageActivator.getDefault().createTempFile("PdfImg", ".jpg"); //$NON-NLS-1$//$NON-NLS-2$ tempFiles.add(jpegFile); try (FileOutputStream out = new FileOutputStream(jpegFile)) { zimage.saveToStream(monitor, true, ZImage.UNCROPPED, SWT.DEFAULT, SWT.DEFAULT, out, SWT.IMAGE_JPEG, jpegQuality); } zimage.dispose(); com.itextpdf.text.Image pdfImage = com.itextpdf.text.Image.getInstance(jpegFile.getPath()); double factor = Math.min(imageWidth / bounds.width, imageHeight / bounds.height); pdfImage.setInterpolation(true); pdfImage.scaleToFit((float) (bounds.width * factor), (float) (bounds.height * factor)); cell = new PdfPCell(pdfImage, false); cell.setHorizontalAlignment(Element.ALIGN_CENTER); monitor.worked(1); } cell.setFixedHeight((float) imageHeight); cell.setBorderWidth(0); table.addCell(cell); } renderCaptions(pageNo, seqNo, pageItem, table, ni, layout.getCaption1()); renderCaptions(pageNo, seqNo, pageItem, table, ni, layout.getCaption2()); pageItem += layout.getColumns(); seqNo += layout.getColumns(); if (verticalGap > 0 && i < rows - 1) for (int j = 0; j < layout.getColumns(); j++) { PdfPCell cell = new PdfPCell(); cell.setFixedHeight(verticalGap); cell.setBorderWidth(0); table.addCell(cell); } } table.setWidthPercentage(100f); document.add(table); if (!layout.getFooter().isEmpty()) { String footer = PageProcessor.computeTitle(layout.getFooter(), fileName, now, assets.size(), pageNo, pages, collection, meta); Paragraph p = new Paragraph(footer, FontFactory.getFont(FontFactory.HELVETICA, subtitleSize, Font.NORMAL, BaseColor.DARK_GRAY)); p.setAlignment(Element.ALIGN_CENTER); p.setSpacingBefore(upperWaste / 2 + footerLead); document.add(p); } } } private void renderCaptions(int pageNo, int seqNo, int pageItem, PdfPTable table, int ni, String caption) { if (!caption.isEmpty()) { for (int j = 0; j < layout.getColumns(); j++) { int a = (pageNo - 1) * imagesPerPage + ni + j; PdfPCell cell; if (a >= assets.size()) cell = new PdfPCell(); else { String cc = PageProcessor.computeCaption(caption, Constants.PI_ALL, assets.get(a), collection, seqNo + j + 1, pageItem + j + 1); Paragraph p = new Paragraph(cc, FontFactory.getFont(FontFactory.HELVETICA, fontSize, Font.NORMAL, BaseColor.DARK_GRAY)); p.setSpacingBefore(0); cell = new PdfPCell(p); cell.setHorizontalAlignment(Element.ALIGN_CENTER); } cell.setBorderWidth(0); table.addCell(cell); } } } public void setSubject(String subject) { this.subject = subject; } public void setMessage(String message) { this.message = message; } }