/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.course.nodes.cl.ui; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletResponse; import javax.xml.transform.TransformerException; import org.apache.pdfbox.exceptions.COSVisitorException; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.translator.Translator; import org.olat.core.id.UserConstants; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.filter.FilterFactory; import org.olat.core.util.pdf.PdfDocument; import org.olat.course.nodes.cl.model.Checkbox; import org.olat.course.nodes.cl.model.CheckboxList; import org.olat.user.propertyhandlers.UserPropertyHandler; /** * * Initial date: 17.02.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class CheckedPDFExport extends PdfDocument implements MediaResource { private static final OLog log = Tracing.createLoggerFor(CheckedPDFExport.class); private final String filename; private String courseTitle; private String courseNodeTitle; private String author; private final boolean withScore; private final Translator translator; private final List<UserPropertyHandler> userPropertyHandlers; private int firstNameIndex, lastNameIndex, institutionalUserIdentifierIndex; private int numOfCols = 0; public CheckedPDFExport(String filename, Translator translator, boolean withScore, List<UserPropertyHandler> userPropertyHandlers) throws IOException { super(translator.getLocale()); marginTopBottom = 62.0f; marginLeftRight = 62.0f; this.filename = filename; this.translator = translator; this.withScore = withScore; this.userPropertyHandlers = userPropertyHandlers; lastNameIndex = findPropertyIndex(UserConstants.LASTNAME, userPropertyHandlers); firstNameIndex = findPropertyIndex(UserConstants.FIRSTNAME, userPropertyHandlers); institutionalUserIdentifierIndex = findPropertyIndex(UserConstants.INSTITUTIONALUSERIDENTIFIER, userPropertyHandlers); } private int findPropertyIndex(String propertyName, List<UserPropertyHandler> userPropHandlers) { int i=0; int index = -1; for(UserPropertyHandler userPropertyHandler:userPropHandlers) { if(propertyName.equals(userPropertyHandler.getName())) { index = i; numOfCols++; } i++; } return index; } private String findHeader(String propertyName, List<UserPropertyHandler> userPropHandlers) { String header = null; for(UserPropertyHandler userPropertyHandler:userPropHandlers) { if(propertyName.equals(userPropertyHandler.getName())) { header = translator.translate(userPropertyHandler.i18nColumnDescriptorLabelKey()); } } return header; } public String getCourseTitle() { return courseTitle; } public void setCourseTitle(String courseTitle) { this.courseTitle = courseTitle; } public String getCourseNodeTitle() { return courseNodeTitle; } public void setCourseNodeTitle(String courseNodeTitle) { this.courseNodeTitle = courseNodeTitle; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } @Override public boolean acceptRanges() { return false; } @Override public String getContentType() { return "application/pdf"; } @Override public Long getSize() { return null; } @Override public InputStream getInputStream() { return null; } @Override public Long getLastModified() { return null; } @Override public void prepare(HttpServletResponse hres) { try { hres.setHeader("Content-Disposition","attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(filename)); hres.setHeader("Content-Description",StringHelper.urlEncodeUTF8(filename)); document.save(hres.getOutputStream()); } catch (COSVisitorException | IOException e) { log.error("", e); } } @Override public void release() { try { close(); } catch (IOException e) { log.error("", e); } } public void create(CheckboxList checkboxList, List<CheckListAssessmentRow> rows) throws IOException, COSVisitorException, TransformerException { addMetadata(courseNodeTitle, courseTitle, author); int i=0; for(Checkbox checkbox:checkboxList.getList()) { create(checkbox, i++, rows); } addPageNumbers(); } private void create(Checkbox checkbox, int checkboxIndex, List<CheckListAssessmentRow> rows) throws IOException, COSVisitorException, TransformerException { addPage(); if(StringHelper.containsNonWhitespace(courseTitle)) { addParagraph(courseTitle, 16, true, width); } if(StringHelper.containsNonWhitespace(courseNodeTitle)) { addParagraph(courseNodeTitle, 14, true, width); } String text = checkbox.getTitle(); if(StringHelper.containsNonWhitespace(text)) { if(withScore && checkbox.getPoints() != null) { String[] points = new String[]{ Float.toString(checkbox.getPoints().floatValue() )}; text += " " + translator.translate("box.points.info", points); } addParagraph(text, 12f, true, width); } String description = checkbox.getDescription(); if(StringHelper.containsNonWhitespace(description)) { description = FilterFactory.getHtmlTagAndDescapingFilter().filter(description); addParagraph(description, 10f, width); } String msg = translator.translate("done.by"); addParagraph(msg, 10f, width); float cellMargin = 5f; float fontSize = 10f; String[] headers = getHeaders(); List<CheckListAssessmentRow> content = getRows(checkboxIndex, rows); int numOfRows = content.size(); if(numOfRows == 0) { closePage(); } else { for(int offset=0; offset<numOfRows; ) { offset += drawTable(headers, content, offset, fontSize, cellMargin); closePage(); if(offset<numOfRows) { addPage(); } } } } private List<CheckListAssessmentRow> getRows(int checkboxIndex, List<CheckListAssessmentRow> rows) { int numOfRows = rows.size(); List<CheckListAssessmentRow> filteredRows = new ArrayList<>(rows.size()); for(int i=0; i<numOfRows; i++) { CheckListAssessmentRow row = rows.get(i); Boolean[] checks = row.getChecked(); if(checks != null && checks.length > checkboxIndex && checks[checkboxIndex] != null && checks[checkboxIndex].booleanValue()) { filteredRows.add(row); } } return filteredRows; } private String[] getHeaders() { List<String> headers = new ArrayList<>(); if(firstNameIndex >= 0) { headers.add(findHeader(UserConstants.FIRSTNAME, userPropertyHandlers)); } if(lastNameIndex >= 0) { headers.add(findHeader(UserConstants.LASTNAME, userPropertyHandlers)); } if(institutionalUserIdentifierIndex >= 0) { headers.add(findHeader(UserConstants.INSTITUTIONALUSERIDENTIFIER, userPropertyHandlers)); } return headers.toArray(new String[headers.size()]); } public int drawTable(String[] headers, List<CheckListAssessmentRow> content, int offset, float fontSize, float cellMargin) throws IOException { float tableWidth = width; float rowHeight = (lineHeightFactory * fontSize) + (2 * cellMargin); float headerHeight = rowHeight; float availableHeight = currentY - marginTopBottom - headerHeight; float numOfAvailableRows = availableHeight / rowHeight; int possibleRows = Math.round(numOfAvailableRows); int end = Math.min(offset + possibleRows, content.size()); int rows = end - offset; float tableHeight = (rowHeight * rows) + headerHeight; float colWidth = tableWidth / numOfCols; // draw the rows float y = currentY; float nexty = currentY; drawLine(marginLeftRight, nexty, marginLeftRight + tableWidth, nexty, 0.5f); nexty -= headerHeight; for (int i = 0; i <= rows; i++) { drawLine(marginLeftRight, nexty, marginLeftRight + tableWidth, nexty, 0.5f); nexty -= rowHeight; } // draw the columns float nextx = marginLeftRight; for (int i=0; i<=numOfCols; i++) { drawLine(nextx, y, nextx, y - tableHeight, 0.5f); nextx += colWidth; } // now add the text // draw the headers float textx = marginLeftRight + cellMargin; float texty = currentY; for (int h=0; h<numOfCols; h++) { String text = headers[h]; if(text == null) { text = ""; } currentContentStream.beginText(); currentContentStream.setFont(font, fontSize); currentContentStream.moveTextPositionByAmount(textx, texty - headerHeight + cellMargin); currentContentStream.drawString(text); currentContentStream.endText(); textx += colWidth; } currentY -= headerHeight; textx = marginLeftRight + cellMargin; texty = currentY - 15; for (int i=offset; i<end; i++) { CheckListAssessmentRow rowContent = content.get(i); if(rowContent == null) continue; if(firstNameIndex >= 0) { String text = rowContent.getIdentityProp(firstNameIndex); drawTextAtMovedPositionByAmount(text, fontSize, textx, texty); textx += colWidth; } if(lastNameIndex >= 0) { String text = rowContent.getIdentityProp(lastNameIndex); drawTextAtMovedPositionByAmount(text, fontSize, textx, texty); textx += colWidth; } if(institutionalUserIdentifierIndex >= 0) { String text = rowContent.getIdentityProp(institutionalUserIdentifierIndex); drawTextAtMovedPositionByAmount(text, fontSize, textx, texty); textx += colWidth; } texty -= rowHeight; textx = marginLeftRight + cellMargin; } return rows; } }