/**
*
* Copyright
* 2009-2015 Jayway Products AB
* 2016-2017 Föreningen Sambruk
*
* Licensed under AGPL, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/agpl.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package se.streamsource.streamflow.web.application.pdf;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.qi4j.api.common.ConstructionException;
import org.qi4j.api.common.Optional;
import org.qi4j.api.configuration.Configuration;
import org.qi4j.api.injection.scope.Service;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.injection.scope.This;
import org.qi4j.api.io.Outputs;
import org.qi4j.api.mixin.Mixins;
import org.qi4j.api.service.Activatable;
import org.qi4j.api.service.ServiceComposite;
import org.qi4j.api.structure.Module;
import org.qi4j.api.util.DateFunctions;
import se.streamsource.streamflow.api.administration.form.AttachmentFieldValue;
import se.streamsource.streamflow.api.administration.form.DateFieldValue;
import se.streamsource.streamflow.api.administration.form.GeoLocationFieldValue;
import se.streamsource.streamflow.api.administration.form.LocationDTO;
import se.streamsource.streamflow.api.workspace.cases.CaseOutputConfigDTO;
import se.streamsource.streamflow.api.workspace.cases.form.AttachmentFieldSubmission;
import se.streamsource.streamflow.web.domain.entity.caze.CaseEntity;
import se.streamsource.streamflow.web.domain.entity.form.FieldEntity;
import se.streamsource.streamflow.web.domain.interaction.gtd.CaseId;
import se.streamsource.streamflow.web.domain.interaction.gtd.Ownable;
import se.streamsource.streamflow.web.domain.interaction.gtd.Owner;
import se.streamsource.streamflow.web.domain.structure.SubmittedFieldValue;
import se.streamsource.streamflow.web.domain.structure.attachment.AttachedFile;
import se.streamsource.streamflow.web.domain.structure.attachment.CasePdfTemplate;
import se.streamsource.streamflow.web.domain.structure.attachment.DefaultPdfTemplate;
import se.streamsource.streamflow.web.domain.structure.form.Form;
import se.streamsource.streamflow.web.domain.structure.form.Page;
import se.streamsource.streamflow.web.domain.structure.form.SubmittedFormValue;
import se.streamsource.streamflow.web.domain.structure.form.SubmittedPageValue;
import se.streamsource.streamflow.web.domain.structure.organization.Organization;
import se.streamsource.streamflow.web.domain.structure.organization.OwningOrganization;
import se.streamsource.streamflow.web.infrastructure.attachment.AttachmentStore;
import se.streamsource.streamflow.web.domain.util.FormVisibilityRuleValidator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;
import static se.streamsource.streamflow.util.Strings.*;
/**
* Service for Pdf generation from both Case and SubmittedForms
*/
@Mixins(PdfGeneratorService.Mixin.class)
public interface PdfGeneratorService
extends Configuration, ServiceComposite, Activatable
{
PDDocument generateCasePdf( CaseEntity caseEntity, CaseOutputConfigDTO configOutput, @Optional Locale locale )
throws Throwable;
PDDocument generateSubmittedFormPdf( SubmittedFormValue value, CaseId.Data id, @Optional String templateUri, @Optional Locale locale )
throws Throwable;
abstract class Mixin
implements PdfGeneratorService
{
@Structure
Module module;
@Service
AttachmentStore store;
@This
Configuration<PdfGeneratorConfiguration> config;
public PDDocument generateCasePdf( CaseEntity caseEntity, CaseOutputConfigDTO configOutput, Locale locale )
throws Throwable
{
Ownable.Data project = (Ownable.Data) caseEntity.owner().get();
Owner ou = project.owner().get();
Organization org = ((OwningOrganization) ou).organization().get();
AttachedFile.Data template = (AttachedFile.Data) ((CasePdfTemplate.Data) org).casePdfTemplate().get();
if (template == null)
{
template = (AttachedFile.Data) ((DefaultPdfTemplate.Data) org).defaultPdfTemplate().get();
}
String uri = null;
if (template != null)
{
uri = template.uri().get();
}
PdfGeneratorConfiguration pdfConfig = config.configuration();
PdfDocument document = new PdfDocument( PDPage.PAGE_SIZE_A4,
pdfConfig.headerMargin().get(), pdfConfig.footerMargin().get(),
pdfConfig.leftMargin().get(), pdfConfig.rightMargin().get() );
CasePdfGenerator exporter = module.objectBuilderFactory()
.newObjectBuilder( CasePdfGenerator.class )
.use( configOutput, uri, locale != null ? locale : new Locale( config.configuration().language().get() ), document )
.newInstance();
caseEntity.outputCase(exporter);
return exporter.getPdf();
}
public PDDocument generateSubmittedFormPdf( SubmittedFormValue value, CaseId.Data id, String templateUri, Locale locale ) throws Throwable
{
FormVisibilityRuleValidator visibilityValidator = module.objectBuilderFactory()
.newObjectBuilder( FormVisibilityRuleValidator.class ).use( value ).newInstance();
ResourceBundle bundle = ResourceBundle.getBundle(
CasePdfGenerator.class.getName(), locale );
PdfGeneratorConfiguration pdfConfig = config.configuration();
PdfDocument document = new PdfDocument( PDPage.PAGE_SIZE_A4,
pdfConfig.headerMargin().get(), pdfConfig.footerMargin().get(),
pdfConfig.leftMargin().get(), pdfConfig.rightMargin().get() );
document.init();
PdfFont h1Font = new PdfFont( PDType1Font.HELVETICA_BOLD, 16 );
PdfFont h2Font = new PdfFont( PDType1Font.HELVETICA_BOLD, 14 );
PdfFont valueFont = new PdfFont( PDType1Font.HELVETICA, 12 );
PdfFont descFont = new PdfFont( PDType1Font.HELVETICA_OBLIQUE, 10 );
PdfFont pageFont = new PdfFont( PDType1Font.HELVETICA_BOLD_OBLIQUE, 14 );
Form form = module.unitOfWorkFactory().currentUnitOfWork().get( Form.class, value.form().get().identity() );
document.print( bundle.getString( "caseid") + ": " + id.caseId().get(), h1Font);
document.print( form.getDescription(), h2Font );
document.print( bundle.getString( "submission_date") + ": " + DateFormat.getDateInstance( DateFormat.MEDIUM, locale ).format( value.submissionDate().get() ), descFont );
document.line();
for (SubmittedPageValue submittedPageValue : value.pages().get())
{
if( !visibilityValidator.visible( submittedPageValue ) ) continue;
Page page = module.unitOfWorkFactory().currentUnitOfWork().get( Page.class, submittedPageValue.page().get().identity() );
document.print( page.getDescription(), pageFont );
// TODO Page breaks
for (SubmittedFieldValue submittedFieldValue : submittedPageValue.fields().get())
{
if( !visibilityValidator.visible( submittedFieldValue ) ) continue;
FieldEntity field = module.unitOfWorkFactory().currentUnitOfWork().get(FieldEntity.class, submittedFieldValue.field().get().identity());
document.print(field.getDescription() + ":", h2Font);
if (field.fieldValue().get() instanceof DateFieldValue && !empty( submittedFieldValue.value().get() ))
{
Date date = DateFunctions.fromString( submittedFieldValue.value().get() );
document.print(DateFormat.getDateInstance( DateFormat.MEDIUM, locale ).format( date ), valueFont);
} else if ( field.fieldValue().get() instanceof GeoLocationFieldValue )
{
LocationDTO locationDTO = module.valueBuilderFactory().newValueFromJSON( LocationDTO.class, submittedFieldValue.value().get() );
document.print( locationDTO.street().get() + ", " + locationDTO.zipcode().get() + ", " + locationDTO.city().get(), valueFont);
} else if (field.fieldValue().get() instanceof AttachmentFieldValue)
{
try
{
AttachmentFieldSubmission attachment = module.valueBuilderFactory().newValueFromJSON(AttachmentFieldSubmission.class, submittedFieldValue.value().get());
document.print(attachment.name().get(), valueFont);
} catch (ConstructionException e)
{
//ignore
}
} else
{
document.print( submittedFieldValue.value().get(), valueFont );
}
document.print("", valueFont);
}
}
PDDocument submittedFormPdf = document.closeAndReturn();
submittedFormPdf.getDocumentInformation().setCreator( "Streamflow" );
Calendar calendar = Calendar.getInstance();
calendar.setTime( value.submissionDate().get() );
submittedFormPdf.getDocumentInformation().setCreationDate( calendar );
submittedFormPdf.getDocumentInformation().setTitle( form.getDescription() );
if (templateUri != null)
{
String attachmentId = new URI( templateUri ).getSchemeSpecificPart();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
store.attachment(attachmentId).transferTo( Outputs.byteBuffer( baos ));
Underlay underlay = new Underlay();
submittedFormPdf = underlay.underlay( submittedFormPdf, new ByteArrayInputStream(baos.toByteArray()) );
}
return submittedFormPdf;
}
public void activate() throws Exception
{
config.configuration();
}
public void passivate() throws Exception
{
}
}
}