Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object Versions {

// Latest Version:
// Visit https://central.sonatype.com and search for: g:org.jodconverter a:jodconverter-core
const val jodConverter = "4.4.7"
const val jodConverter = "4.4.9"

// Latest Version:
// Visit https://central.sonatype.com and search for: g:commons-fileupload a:commons-fileupload
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.jodconverter.sample.rest;

import com.sun.star.beans.PropertyValue;
import com.sun.star.frame.XController;
import com.sun.star.frame.XDispatchHelper;
import com.sun.star.frame.XDispatchProvider;
import com.sun.star.frame.XFrame;
import com.sun.star.frame.XModel;
import com.sun.star.lang.XComponent;
import com.sun.star.uno.XComponentContext;
import org.jodconverter.core.office.OfficeContext;
import org.jodconverter.local.filter.Filter;
import org.jodconverter.local.filter.FilterChain;
import org.jodconverter.local.office.LocalOfficeContext;
import org.jodconverter.local.office.utils.Lo;
import org.jodconverter.local.office.utils.Write;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A {@link Filter} that accepts all tracked changes (redlines) in a Writer document before it is
* passed further down the conversion chain (e.g. exported to PDF).
*
* <p>By default LibreOffice exports a document with its tracked changes preserved, so the resulting
* PDF may still contain insertion/deletion markup. This filter applies all revisions in memory by
* dispatching the {@code .uno:AcceptAllTrackedChanges} command, producing the clean "final" view.
*
* <p>Only Writer (text) documents are affected; other document types pass through untouched.
*/
public class AcceptAllTrackedChangesFilter implements Filter {

private static final Logger LOGGER =
LoggerFactory.getLogger(AcceptAllTrackedChangesFilter.class);

private static final String ACCEPT_ALL_TRACKED_CHANGES = ".uno:AcceptAllTrackedChanges";

@Override
public void doFilter(
final OfficeContext context, final XComponent document, final FilterChain chain)
throws Exception {

if (Write.isText(document)) {
LOGGER.debug("Applying AcceptAllTrackedChangesFilter to text document");
acceptAllTrackedChanges(context, document);
} else {
LOGGER.debug("Skipping AcceptAllTrackedChangesFilter (document is not a text document)");
}

// Continue the conversion chain.
chain.doFilter(context, document);
}

private static void acceptAllTrackedChanges(
final OfficeContext context, final XComponent document) {

final XComponentContext componentContext =
((LocalOfficeContext) context).getComponentContext();
if (componentContext == null) {
LOGGER.warn("No component context available; cannot accept tracked changes");
return;
}

final XModel model = Lo.qi(XModel.class, document);
final XController controller = model.getCurrentController();
if (controller == null) {
LOGGER.warn("No controller available; cannot accept tracked changes");
return;
}

final XFrame frame = controller.getFrame();
if (frame == null) {
LOGGER.warn("No frame available; cannot accept tracked changes");
return;
}

final XDispatchProvider dispatchProvider = Lo.qi(XDispatchProvider.class, frame);
final XDispatchHelper dispatchHelper =
Lo.createInstance(
componentContext, XDispatchHelper.class, "com.sun.star.frame.DispatchHelper");
if (dispatchHelper == null) {
LOGGER.warn("Could not create DispatchHelper; cannot accept tracked changes");
return;
}

dispatchHelper.executeDispatch(
dispatchProvider, ACCEPT_ALL_TRACKED_CHANGES, "", 0, new PropertyValue[0]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -49,6 +50,15 @@ public class ConverterController {
@Autowired
private ParameterDecoder parameterDecoder;

/**
* Default for whether all tracked changes should be accepted before conversion. Applied when a
* request does not explicitly set the {@code acceptTrackedChanges} parameter. Configurable via
* the {@code jodconverter.accept-tracked-changes} property (env {@code
* JODCONVERTER_ACCEPT_TRACKED_CHANGES}).
*/
@Value("${jodconverter.accept-tracked-changes:false}")
private boolean acceptTrackedChangesByDefault;

/**
* Creates a new controller.
*
Expand Down Expand Up @@ -82,11 +92,17 @@ public ConverterController(final OfficeManager officeManager) {
description = "The document format to convert the input document to.",
required = true)
@RequestParam(name = "format") final String convertToFormat,
@Parameter(
description =
"Whether to accept all tracked changes before converting. Defaults to the"
+ " server configuration when omitted.")
@RequestParam(name = "acceptTrackedChanges", required = false)
final Boolean acceptTrackedChanges,
@Parameter(description = "The custom options to apply to the conversion.")
@RequestParam(required = false) final Map<String, String> parameters) {

LOGGER.debug("convertUsingRequestParam > Converting file to {}", convertToFormat);
return convert(inputFile, convertToFormat, parameters);
return convert(inputFile, convertToFormat, acceptTrackedChanges, parameters);
}

@Operation(
Expand All @@ -112,16 +128,23 @@ public ConverterController(final OfficeManager officeManager) {
description = "The document format to convert the input document to.",
required = true)
@PathVariable(name = "format") final String convertToFormat,
@Parameter(
description =
"Whether to accept all tracked changes before converting. Defaults to the"
+ " server configuration when omitted.")
@RequestParam(name = "acceptTrackedChanges", required = false)
final Boolean acceptTrackedChanges,
@Parameter(description = "The custom options to apply to the conversion.")
@RequestParam(required = false) final Map<String, String> parameters) {

LOGGER.debug("convertUsingPathVariable > Converting file to {}", convertToFormat);
return convert(inputFile, convertToFormat, parameters);
return convert(inputFile, convertToFormat, acceptTrackedChanges, parameters);
}

private ResponseEntity<Object> convert(
final MultipartFile inputFile,
final String outputFormat,
final Boolean acceptTrackedChanges,
final Map<String, String> parameters) {

if (inputFile.isEmpty()) {
Expand All @@ -145,13 +168,21 @@ private ResponseEntity<Object> convert(
final Map<String, Object> storeProperties = new HashMap<>();
parameterDecoder.decodeParameters(parameters, loadProperties, storeProperties);

// Create a converter with the properties.
final DocumentConverter converter =
// Create a converter with the properties, optionally accepting all tracked changes
// (redlines) before the document is exported.
final LocalConverter.Builder converterBuilder =
LocalConverter.builder()
.officeManager(officeManager)
.loadProperties(loadProperties)
.storeProperties(storeProperties)
.build();
.storeProperties(storeProperties);

final boolean acceptChanges =
acceptTrackedChanges != null ? acceptTrackedChanges : acceptTrackedChangesByDefault;
if (acceptChanges) {
converterBuilder.filterChain(new AcceptAllTrackedChangesFilter());
}

final DocumentConverter converter = converterBuilder.build();

// Convert...
converter.convert(inputFile.getInputStream()).to(baos).as(targetFormat).execute();
Expand Down
4 changes: 4 additions & 0 deletions samples/spring-boot-rest/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ springdoc:
path: /swagger-ui.html

jodconverter:
# Accept all tracked changes (redlines) before converting a document (e.g. docx -> pdf).
# Can be overridden per request with the `acceptTrackedChanges` request parameter.
# Set JODCONVERTER_ACCEPT_TRACKED_CHANGES=true to enable globally.
accept-tracked-changes: ${JODCONVERTER_ACCEPT_TRACKED_CHANGES:false}
local:
enabled: true
port-numbers: 2002,2003
Expand Down