Merged feature/dk-misc-20250327 into dev

This commit is contained in:
2025-04-03 14:24:52 -05:00
4 changed files with 111 additions and 37 deletions

View File

@ -151,16 +151,21 @@
<version>2.3</version> <version>2.3</version>
</dependency> </dependency>
<!-- the next 2 deps are for html to pdf - per https://www.baeldung.com/java-html-to-pdf --> <!-- the next 3 deps are for html to pdf -->
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<version>1.15.3</version> <version>1.15.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.xhtmlrenderer</groupId> <groupId>com.openhtmltopdf</groupId>
<artifactId>flying-saucer-pdf-openpdf</artifactId> <artifactId>openhtmltopdf-core</artifactId>
<version>9.1.22</version> <version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>1.0.10</version>
</dependency> </dependency>
<!-- the next 3 deps are being added for google drive support --> <!-- the next 3 deps are being added for google drive support -->

View File

@ -26,22 +26,31 @@ import java.nio.file.Path;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction; import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfInput; import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfInput;
import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfOutput; import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfOutput;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.openhtmltopdf.css.constants.IdentValue;
import com.openhtmltopdf.pdfboxout.PdfBoxFontResolver;
import com.openhtmltopdf.pdfboxout.PdfBoxRenderer;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.xhtmlrenderer.layout.SharedContext;
import org.xhtmlrenderer.pdf.ITextRenderer;
/******************************************************************************* /*******************************************************************************
** Action to convert a string of HTML to a PDF! ** Action to convert a string of HTML to a PDF!
** **
** Much credit to https://www.baeldung.com/java-html-to-pdf ** Much credit to https://www.baeldung.com/java-html-to-pdf
*******************************************************************************/ **
** Updated in March 2025 to go from flying-saucer-pdf-openpdf lib to openhtmltopdf,
** mostly to get support for max-height on images...
********************************************************************************/
public class ConvertHtmlToPdfAction extends AbstractQActionFunction<ConvertHtmlToPdfInput, ConvertHtmlToPdfOutput> public class ConvertHtmlToPdfAction extends AbstractQActionFunction<ConvertHtmlToPdfInput, ConvertHtmlToPdfOutput>
{ {
private static final QLogger LOG = QLogger.getLogger(ConvertHtmlToPdfAction.class);
/******************************************************************************* /*******************************************************************************
** **
@ -62,31 +71,32 @@ public class ConvertHtmlToPdfAction extends AbstractQActionFunction<ConvertHtmlT
////////////////////////////// //////////////////////////////
// convert the XHTML to PDF // // convert the XHTML to PDF //
////////////////////////////// //////////////////////////////
ITextRenderer renderer = new ITextRenderer(); PdfRendererBuilder builder = new PdfRendererBuilder();
SharedContext sharedContext = renderer.getSharedContext(); builder.toStream(input.getOutputStream());
sharedContext.setPrint(true); builder.useFastMode();
sharedContext.setInteractive(false); builder.withHtmlContent(document.html(), input.getBasePath() == null ? "./" : input.getBasePath().toUri().toString());
if(input.getBasePath() != null) try(PdfBoxRenderer pdfBoxRenderer = builder.buildPdfRenderer())
{ {
String baseUrl = input.getBasePath().toUri().toURL().toString(); pdfBoxRenderer.layout();
renderer.setDocumentFromString(document.html(), baseUrl); pdfBoxRenderer.getSharedContext().setPrint(true);
} pdfBoxRenderer.getSharedContext().setInteractive(false);
else
{
renderer.setDocumentFromString(document.html());
}
////////////////////////////////////////////////// for(Map.Entry<String, Path> entry : CollectionUtils.nonNullMap(input.getCustomFonts()).entrySet())
// register any custom fonts the input supplied // {
////////////////////////////////////////////////// LOG.warn("Note: Custom fonts appear to not be working in this class at this time...");
for(Map.Entry<String, Path> entry : CollectionUtils.nonNullMap(input.getCustomFonts()).entrySet()) pdfBoxRenderer.getFontResolver().addFont(
{ entry.getValue().toAbsolutePath().toFile(), // Path to the TrueType font file
renderer.getFontResolver().addFont(entry.getValue().toAbsolutePath().toString(), entry.getKey(), "UTF-8", true, null); entry.getKey(), // Font family name to use in CSS
} 400, // Font weight (e.g., 400 for normal, 700 for bold)
IdentValue.NORMAL, // Font style (e.g., NORMAL, ITALIC)
true, // Whether to subset the font
PdfBoxFontResolver.FontGroup.MAIN // ??
);
}
renderer.layout(); pdfBoxRenderer.createPDF();
renderer.createPDF(input.getOutputStream()); }
return (output); return (output);
} }

View File

@ -468,7 +468,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public String getValueString(String fieldName) public String getValueString(String fieldName)
{ {
return (ValueUtils.getValueAsString(values.get(fieldName))); return (ValueUtils.getValueAsString(getValue(fieldName)));
} }
@ -479,7 +479,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public Integer getValueInteger(String fieldName) public Integer getValueInteger(String fieldName)
{ {
return (ValueUtils.getValueAsInteger(values.get(fieldName))); return (ValueUtils.getValueAsInteger(getValue(fieldName)));
} }
@ -490,7 +490,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public Long getValueLong(String fieldName) public Long getValueLong(String fieldName)
{ {
return (ValueUtils.getValueAsLong(values.get(fieldName))); return (ValueUtils.getValueAsLong(getValue(fieldName)));
} }
@ -500,7 +500,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public BigDecimal getValueBigDecimal(String fieldName) public BigDecimal getValueBigDecimal(String fieldName)
{ {
return (ValueUtils.getValueAsBigDecimal(values.get(fieldName))); return (ValueUtils.getValueAsBigDecimal(getValue(fieldName)));
} }
@ -510,7 +510,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public Boolean getValueBoolean(String fieldName) public Boolean getValueBoolean(String fieldName)
{ {
return (ValueUtils.getValueAsBoolean(values.get(fieldName))); return (ValueUtils.getValueAsBoolean(getValue(fieldName)));
} }
@ -520,7 +520,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public LocalTime getValueLocalTime(String fieldName) public LocalTime getValueLocalTime(String fieldName)
{ {
return (ValueUtils.getValueAsLocalTime(values.get(fieldName))); return (ValueUtils.getValueAsLocalTime(getValue(fieldName)));
} }
@ -530,7 +530,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public LocalDate getValueLocalDate(String fieldName) public LocalDate getValueLocalDate(String fieldName)
{ {
return (ValueUtils.getValueAsLocalDate(values.get(fieldName))); return (ValueUtils.getValueAsLocalDate(getValue(fieldName)));
} }
@ -540,7 +540,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public byte[] getValueByteArray(String fieldName) public byte[] getValueByteArray(String fieldName)
{ {
return (ValueUtils.getValueAsByteArray(values.get(fieldName))); return (ValueUtils.getValueAsByteArray(getValue(fieldName)));
} }
@ -550,7 +550,7 @@ public class QRecord implements Serializable
*******************************************************************************/ *******************************************************************************/
public Instant getValueInstant(String fieldName) public Instant getValueInstant(String fieldName)
{ {
return (ValueUtils.getValueAsInstant(values.get(fieldName))); return (ValueUtils.getValueAsInstant(getValue(fieldName)));
} }

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.data;
import java.io.Serializable; import java.io.Serializable;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -198,4 +199,62 @@ public class QRecordWithJoinedRecords extends QRecord
return (rs); return (rs);
} }
/***************************************************************************
**
***************************************************************************/
@Override
public Map<String, List<QRecord>> getAssociatedRecords()
{
return mainRecord.getAssociatedRecords();
}
/***************************************************************************
**
***************************************************************************/
@Override
public QRecord withAssociatedRecord(String name, QRecord associatedRecord)
{
mainRecord.withAssociatedRecord(name, associatedRecord);
return (this);
}
/***************************************************************************
**
***************************************************************************/
@Override
public QRecord withAssociatedRecords(Map<String, List<QRecord>> associatedRecords)
{
mainRecord.withAssociatedRecords(associatedRecords);
return (this);
}
/***************************************************************************
**
***************************************************************************/
@Override
public void setAssociatedRecords(Map<String, List<QRecord>> associatedRecords)
{
mainRecord.setAssociatedRecords(associatedRecords);
}
/***************************************************************************
**
***************************************************************************/
@Override
public QRecord withAssociatedRecords(String name, List<QRecord> associatedRecords)
{
mainRecord.withAssociatedRecords(name, associatedRecords);
return (this);
}
} }