Skip to content

Commit

Permalink
XWIKI-22335: Cannot import a ppt with LibreOffice 24.2.5
Browse files Browse the repository at this point in the history
  * Fix existing comments in DefaultPresentationBuilder
  * Provide new properties in xwiki.properties
  * Fallback in document-formats.js in case custom-document-formats is
    not used for backward compatibility
  * Provide unit test
  • Loading branch information
surli committed Sep 26, 2024
1 parent cc39e4d commit ebb3b9f
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,7 @@ protected OfficeConverterResult importPresentation(InputStream officeFileStream,
Map<String, InputStream> inputStreams = Map.of(inputFileName, officeFileStream);
try {
// The office converter uses the output file name extension to determine the output format/syntax.
// The returned artifacts are of three types: imgX.jpg (slide screen shot), imgX.html (HTML page that
// display the corresponding slide screen shot) and textX.html (HTML page that display the text extracted
// from the corresponding slide). We use "img0.html" as the output file name because the corresponding
// artifact displays a screen shot of the first presentation slide.
// We perform a conversion to PDF to then use a PDF to image conversion.
return this.officeServer.getConverter().convertDocument(inputStreams, inputFileName, "presentation.pdf");
} catch (OfficeConverterException e) {
String message = "Error while converting document [%s] into html.";
Expand All @@ -178,10 +175,8 @@ protected OfficeConverterResult importPresentation(InputStream officeFileStream,
}

/**
* Builds the presentation HTML from the presentation artifacts. There are two types of presentation artifacts:
* slide image and slide text. The returned HTML will display all the slide images. Slide text is currently ignored.
* All artifacts except slide images are removed from {@code presentationArtifacts}. Slide images names are prefixed
* with the given {@code nameSpace} to avoid name conflicts.
* Builds the presentation HTML from the presentation PDF: we convert all PDF page to an image using naming
* convention {@code slideX.imageFormatExtension} where X is the slide number.
*
* @param officeConverterResult the map of presentation artifacts; this method removes some of the presentation
* artifacts and renames others so be aware of the side effects
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@
*/
package org.xwiki.officeimporter.internal.builder;

import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
Expand All @@ -45,19 +49,37 @@
@Singleton
public class PresentationBuilderConfiguration implements Initializable
{
protected static final String DEFAULT_IMAGE_FORMAT = "jpg";
private static final String DEFAULT_IMAGE_FORMAT = "jpg";

private int slideWidth = 1920;

private float quality = 95;

@Inject
@Named("xwikiproperties")
private ConfigurationSource configurationSource;

@Inject
private Logger logger;

@Override
public void initialize() throws InitializationException
{
try (InputStream configurationInput = getClass().getResourceAsStream("/custom-document-formats.json")) {
try {
// For backward compatibility reason we check both custom-document-formats and document-formats.
if (!extractConfigurationFromJsonRegistry("/custom-document-formats.json")) {
extractConfigurationFromJsonRegistry("/document-formats.js");
}
} catch (Exception e) {
this.logger.error("Error when initializing values from document format registry, "
+ "default values will be used.", e);
}
}

private boolean extractConfigurationFromJsonRegistry(String filename) throws IOException
{
AtomicBoolean result = new AtomicBoolean(false);
try (InputStream configurationInput = getClass().getResourceAsStream(filename)) {
if (configurationInput != null) {
// Load the configuration from the JSON file
ObjectMapper objectMapper = new ObjectMapper();
Expand All @@ -72,16 +94,16 @@ public void initialize() throws InitializationException
.flatMap(storeProperties -> getJsonNode(storeProperties, "PRESENTATION"))
.flatMap(presentationProperties -> getJsonNode(presentationProperties, "FilterData"))
.ifPresent(filterData -> {
result.set(true);
getJsonNode(filterData, "Quality")
.ifPresent(qualityNode -> this.quality = qualityNode.asInt());
getJsonNode(filterData, "Width")
.ifPresent(widthNode -> this.slideWidth = widthNode.asInt());
});
}
}
} catch (Exception e) {
throw new InitializationException("Failed to initialize the PresentationBuilderConfiguration", e);
}
return result.get();
}

private Optional<JsonNode> getJsonNode(JsonNode jsonNode, String fieldName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.xwiki.officeimporter.internal.builder;

import javax.inject.Named;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

/**
* Tests for {@link PresentationBuilderConfiguration}.
*
* @version $Id$
* @since 16.8.0
* @since 16.4.4
* @since 15.10.13
*/
@ComponentTest
class PresentationBuilderConfigurationTest
{
@InjectMockComponents
private PresentationBuilderConfiguration presentationBuilderConfiguration;

@MockComponent
@Named("xwikiproperties")
private ConfigurationSource configurationSource;

@BeforeEach
void setup()
{
when(this.configurationSource.getProperty(anyString(), any(Object.class)))
.thenAnswer(invocationOnMock -> invocationOnMock.getArguments()[1]);
}

@Test
void getQuality()
{
assertEquals(0.56f, this.presentationBuilderConfiguration.getQuality());
when(this.configurationSource.getProperty("officeimporter.presentation.quality", 56f)).thenReturn(66f);
assertEquals(0.66f, this.presentationBuilderConfiguration.getQuality());
when(this.configurationSource.getProperty("officeimporter.presentation.imageFormat", "jpg")).thenReturn("png");
assertEquals(0f, this.presentationBuilderConfiguration.getQuality());
}

@Test
void getSlideWidth()
{
assertEquals(1920, this.presentationBuilderConfiguration.getSlideWidth());
when(this.configurationSource.getProperty("officeimporter.presentation.slideWidth", 1920)).thenReturn(800);
assertEquals(800, this.presentationBuilderConfiguration.getSlideWidth());
}

@Test
void getImageFormat()
{
assertEquals("jpg", this.presentationBuilderConfiguration.getImageFormat());
when(this.configurationSource.getProperty("officeimporter.presentation.imageFormat", "jpg")).thenReturn("png");
assertEquals("png", this.presentationBuilderConfiguration.getImageFormat());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"PRESENTATION": {
"FilterName": "impress_html_Export",
"FilterData": {
"PublishMode": 0
"PublishMode": 0,
"Quality": 56
}
},
"TEXT": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,26 @@ rendering.transformations = $xwikiRenderingTransformations
#-# Default value is 60 seconds
# openoffice.taskExecutionTimeout = 60000

#-# [Since 16.8.0]
#-# [Since 16.4.4]
#-# [Since 15.10.13]
#-# The output image format to use when importing an office presentation in XWiki.
# officeimporter.presentation.imageFormat = jpg

#-# [Since 16.8.0]
#-# [Since 16.4.4]
#-# [Since 15.10.13]
#-# Quality of the output image when importing an office presentation: the quality value is between 0 and 100.
#-# Be aware that this option overrides any filter configuration provided in custom-document-formats.json for html
#-# presentations. Also this configuration is ignored if the image format is png.
# officeimporter.presentation.quality = 95

#-# [Since 16.8.0]
#-# [Since 16.4.4]
#-# [Since 15.10.13]
#-# Width of the output image when importing an office presentation: the value is given in pixel.
# officeimporter.presentation.slideWidth = 1920

#-------------------------------------------------------------------------------------
# Velocity
#-------------------------------------------------------------------------------------
Expand Down

0 comments on commit ebb3b9f

Please sign in to comment.