mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
feat(replace-and-invert-colour): Add CMYK color space conversion with prepress preset for PDF processing (#4494)
This commit is contained in:
parent
4ad039d034
commit
07392ed25e
@ -4,4 +4,5 @@ public enum ReplaceAndInvert {
|
|||||||
HIGH_CONTRAST_COLOR,
|
HIGH_CONTRAST_COLOR,
|
||||||
CUSTOM_COLOR,
|
CUSTOM_COLOR,
|
||||||
FULL_INVERSION,
|
FULL_INVERSION,
|
||||||
|
COLOR_SPACE_CONVERSION,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
package stirling.software.common.util.misc;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.core.io.InputStreamResource;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||||
|
import stirling.software.common.util.ProcessExecutor;
|
||||||
|
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class ColorSpaceConversionStrategy extends ReplaceAndInvertColorStrategy {
|
||||||
|
|
||||||
|
public ColorSpaceConversionStrategy(MultipartFile file, ReplaceAndInvert replaceAndInvert) {
|
||||||
|
super(file, replaceAndInvert);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStreamResource replace() throws IOException {
|
||||||
|
Path tempInputFile = null;
|
||||||
|
Path tempOutputFile = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
tempInputFile = Files.createTempFile("colorspace_input_", ".pdf");
|
||||||
|
tempOutputFile = Files.createTempFile("colorspace_output_", ".pdf");
|
||||||
|
|
||||||
|
Files.write(tempInputFile, getFileInput().getBytes());
|
||||||
|
|
||||||
|
log.info("Starting CMYK color space conversion");
|
||||||
|
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add("gs");
|
||||||
|
command.add("-sDEVICE=pdfwrite");
|
||||||
|
command.add("-dCompatibilityLevel=1.5");
|
||||||
|
command.add("-dPDFSETTINGS=/prepress");
|
||||||
|
command.add("-dNOPAUSE");
|
||||||
|
command.add("-dQUIET");
|
||||||
|
command.add("-dBATCH");
|
||||||
|
command.add("-sProcessColorModel=DeviceCMYK");
|
||||||
|
command.add("-sColorConversionStrategy=CMYK");
|
||||||
|
command.add("-sColorConversionStrategyForImages=CMYK");
|
||||||
|
command.add("-sOutputFile=" + tempOutputFile.toString());
|
||||||
|
command.add(tempInputFile.toString());
|
||||||
|
|
||||||
|
log.debug("Executing Ghostscript command for CMYK conversion: {}", command);
|
||||||
|
|
||||||
|
ProcessExecutorResult result =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
|
||||||
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
|
if (result.getRc() != 0) {
|
||||||
|
log.error(
|
||||||
|
"Ghostscript CMYK conversion failed with return code: {}. Output: {}",
|
||||||
|
result.getRc(),
|
||||||
|
result.getMessages());
|
||||||
|
throw new IOException(
|
||||||
|
"CMYK color space conversion failed: " + result.getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("CMYK color space conversion completed successfully");
|
||||||
|
|
||||||
|
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||||
|
return new InputStreamResource(new ByteArrayInputStream(pdfBytes));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("CMYK color space conversion failed", e);
|
||||||
|
throw new IOException(
|
||||||
|
"Failed to convert PDF to CMYK color space: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
if (tempInputFile != null) {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(tempInputFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to delete temporary input file: {}", tempInputFile, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tempOutputFile != null) {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(tempOutputFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to delete temporary output file: {}", tempOutputFile, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import stirling.software.common.model.api.misc.HighContrastColorCombination;
|
import stirling.software.common.model.api.misc.HighContrastColorCombination;
|
||||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||||
|
import stirling.software.common.util.misc.ColorSpaceConversionStrategy;
|
||||||
import stirling.software.common.util.misc.CustomColorReplaceStrategy;
|
import stirling.software.common.util.misc.CustomColorReplaceStrategy;
|
||||||
import stirling.software.common.util.misc.InvertFullColorStrategy;
|
import stirling.software.common.util.misc.InvertFullColorStrategy;
|
||||||
import stirling.software.common.util.misc.ReplaceAndInvertColorStrategy;
|
import stirling.software.common.util.misc.ReplaceAndInvertColorStrategy;
|
||||||
@ -19,21 +20,17 @@ public class ReplaceAndInvertColorFactory {
|
|||||||
String backGroundColor,
|
String backGroundColor,
|
||||||
String textColor) {
|
String textColor) {
|
||||||
|
|
||||||
if (replaceAndInvertOption == ReplaceAndInvert.CUSTOM_COLOR
|
return switch (replaceAndInvertOption) {
|
||||||
|| replaceAndInvertOption == ReplaceAndInvert.HIGH_CONTRAST_COLOR) {
|
case CUSTOM_COLOR, HIGH_CONTRAST_COLOR ->
|
||||||
|
new CustomColorReplaceStrategy(
|
||||||
return new CustomColorReplaceStrategy(
|
file,
|
||||||
file,
|
replaceAndInvertOption,
|
||||||
replaceAndInvertOption,
|
textColor,
|
||||||
textColor,
|
backGroundColor,
|
||||||
backGroundColor,
|
highContrastColorCombination);
|
||||||
highContrastColorCombination);
|
case FULL_INVERSION -> new InvertFullColorStrategy(file, replaceAndInvertOption);
|
||||||
|
case COLOR_SPACE_CONVERSION ->
|
||||||
} else if (replaceAndInvertOption == ReplaceAndInvert.FULL_INVERSION) {
|
new ColorSpaceConversionStrategy(file, replaceAndInvertOption);
|
||||||
|
};
|
||||||
return new InvertFullColorStrategy(file, replaceAndInvertOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -400,6 +400,7 @@ public class EndpointConfiguration {
|
|||||||
/* Ghostscript */
|
/* Ghostscript */
|
||||||
addEndpointToGroup("Ghostscript", "repair");
|
addEndpointToGroup("Ghostscript", "repair");
|
||||||
addEndpointToGroup("Ghostscript", "compress-pdf");
|
addEndpointToGroup("Ghostscript", "compress-pdf");
|
||||||
|
addEndpointToGroup("Ghostscript", "replace-invert-pdf");
|
||||||
|
|
||||||
/* tesseract */
|
/* tesseract */
|
||||||
addEndpointToGroup("tesseract", "ocr-pdf");
|
addEndpointToGroup("tesseract", "ocr-pdf");
|
||||||
|
|||||||
@ -31,8 +31,8 @@ public class ReplaceAndInvertColorController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Replace-Invert Color PDF",
|
summary = "Replace-Invert Color PDF",
|
||||||
description =
|
description =
|
||||||
"This endpoint accepts a PDF file and option of invert all colors or replace"
|
"This endpoint accepts a PDF file and provides options to invert all colors, replace"
|
||||||
+ " text and background colors. Input:PDF Output:PDF Type:SISO")
|
+ " text and background colors, or convert to CMYK color space for printing. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<InputStreamResource> replaceAndInvertColor(
|
public ResponseEntity<InputStreamResource> replaceAndInvertColor(
|
||||||
@ModelAttribute ReplaceAndInvertColorRequest request) throws IOException {
|
@ModelAttribute ReplaceAndInvertColorRequest request) throws IOException {
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,12 @@ public class ReplaceAndInvertColorRequest extends PDFFile {
|
|||||||
description = "Replace and Invert color options of a pdf.",
|
description = "Replace and Invert color options of a pdf.",
|
||||||
requiredMode = Schema.RequiredMode.REQUIRED,
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
defaultValue = "HIGH_CONTRAST_COLOR",
|
defaultValue = "HIGH_CONTRAST_COLOR",
|
||||||
allowableValues = {"HIGH_CONTRAST_COLOR", "CUSTOM_COLOR", "FULL_INVERSION"})
|
allowableValues = {
|
||||||
|
"HIGH_CONTRAST_COLOR",
|
||||||
|
"CUSTOM_COLOR",
|
||||||
|
"FULL_INVERSION",
|
||||||
|
"COLOR_SPACE_CONVERSION"
|
||||||
|
})
|
||||||
private ReplaceAndInvert replaceAndInvertOption;
|
private ReplaceAndInvert replaceAndInvertOption;
|
||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
|
|||||||
@ -878,6 +878,12 @@ replace-color.selectText.8=Yellow text on black background
|
|||||||
replace-color.selectText.9=Green text on black background
|
replace-color.selectText.9=Green text on black background
|
||||||
replace-color.selectText.10=Choose text Colour
|
replace-color.selectText.10=Choose text Colour
|
||||||
replace-color.selectText.11=Choose background Colour
|
replace-color.selectText.11=Choose background Colour
|
||||||
|
replace-color.selectText.12=Colour Space Conversion (CMYK for Printing)
|
||||||
|
replace-color.selectText.13=CMYK Colour Space Conversion
|
||||||
|
replace-color.selectText.14=This option converts the PDF from RGB colour space to CMYK colour space, which is optimized for professional printing. This process:
|
||||||
|
replace-color.selectText.15=Converts colours to CMYK (Cyan, Magenta, Yellow, Black) colour model used by professional printers
|
||||||
|
replace-color.selectText.16=Optimizes the PDF for print production with prepress settings
|
||||||
|
replace-color.selectText.17=May result in slight colour changes as CMYK has a smaller colour gamut than RGB
|
||||||
replace-color.submit=Replace
|
replace-color.submit=Replace
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
<option value="HIGH_CONTRAST_COLOR" th:text="#{replace-color.selectText.2}" ></option>
|
<option value="HIGH_CONTRAST_COLOR" th:text="#{replace-color.selectText.2}" ></option>
|
||||||
<option value="CUSTOM_COLOR" th:text="#{replace-color.selectText.3}"></option>
|
<option value="CUSTOM_COLOR" th:text="#{replace-color.selectText.3}"></option>
|
||||||
<option value="FULL_INVERSION" th:text="#{replace-color.selectText.4}" selected></option>
|
<option value="FULL_INVERSION" th:text="#{replace-color.selectText.4}" selected></option>
|
||||||
|
<option th:text="#{replace-color.selectText.12}" value="COLOR_SPACE_CONVERSION"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -57,6 +58,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-3" id="color-space-info" style="display: none">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 th:text="#{replace-color.selectText.13}"></h4>
|
||||||
|
<p th:text="#{replace-color.selectText.14}"></p>
|
||||||
|
<ul>
|
||||||
|
<li th:text="#{replace-color.selectText.15}"></li>
|
||||||
|
<li th:text="#{replace-color.selectText.16}"></li>
|
||||||
|
<li th:text="#{replace-color.selectText.17}"></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{replace-color.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{replace-color.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -74,12 +87,15 @@
|
|||||||
$('#high-contrast-options').hide();
|
$('#high-contrast-options').hide();
|
||||||
$('#custom-color-1').hide();
|
$('#custom-color-1').hide();
|
||||||
$('#custom-color-2').hide();
|
$('#custom-color-2').hide();
|
||||||
|
$('#color-space-info').hide();
|
||||||
|
|
||||||
if (selectedOption === "HIGH_CONTRAST_COLOR") {
|
if (selectedOption === "HIGH_CONTRAST_COLOR") {
|
||||||
$('#high-contrast-options').show();
|
$('#high-contrast-options').show();
|
||||||
} else if (selectedOption === "CUSTOM_COLOR") {
|
} else if (selectedOption === "CUSTOM_COLOR") {
|
||||||
$('#custom-color-1').show();
|
$('#custom-color-1').show();
|
||||||
$('#custom-color-2').show();
|
$('#custom-color-2').show();
|
||||||
|
} else if (selectedOption === "COLOR_SPACE_CONVERSION") {
|
||||||
|
$('#color-space-info').show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user