Skip to content

Commit 5e10fb9

Browse files
committed
XWIKI-21763: Disable the server-side image resizing while exporting to PDF
* Using a workaround until we add support for temporarily overwrite properties from a configuration source. * Small refactoring of PDFExportJob to reduce class-fan-out complexity
1 parent 78c9707 commit 5e10fb9

File tree

8 files changed

+154
-61
lines changed

8 files changed

+154
-61
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.export.pdf.internal.job;
21+
22+
import java.util.Arrays;
23+
24+
import javax.inject.Inject;
25+
26+
import org.xwiki.export.pdf.job.PDFExportJobRequest;
27+
import org.xwiki.export.pdf.job.PDFExportJobStatus;
28+
import org.xwiki.job.AbstractJob;
29+
import org.xwiki.job.GroupedJob;
30+
import org.xwiki.job.JobGroupPath;
31+
import org.xwiki.model.reference.EntityReference;
32+
import org.xwiki.security.authorization.AuthorizationManager;
33+
import org.xwiki.security.authorization.Right;
34+
35+
/**
36+
* Base class for PDF export job.
37+
*
38+
* @version $Id$
39+
*/
40+
public abstract class AbstractPDFExportJob extends AbstractJob<PDFExportJobRequest, PDFExportJobStatus>
41+
implements GroupedJob
42+
{
43+
/**
44+
* The PDF export job type.
45+
*/
46+
public static final String JOB_TYPE = "export/pdf";
47+
48+
/**
49+
* Used to check access permissions.
50+
*
51+
* @see #hasAccess(Right, EntityReference)
52+
*/
53+
@Inject
54+
private AuthorizationManager authorization;
55+
56+
@Override
57+
public String getType()
58+
{
59+
return JOB_TYPE;
60+
}
61+
62+
@Override
63+
public JobGroupPath getGroupPath()
64+
{
65+
return new JobGroupPath(Arrays.asList("export", "pdf"));
66+
}
67+
68+
@Override
69+
protected PDFExportJobStatus createNewStatus(PDFExportJobRequest request)
70+
{
71+
return new PDFExportJobStatus(getType(), request, this.observationManager, this.loggerManager);
72+
}
73+
74+
/**
75+
* Check access rights taking into account the job request.
76+
*
77+
* @param right the access right to check
78+
* @param reference the target entity reference
79+
* @return return {@code true} if the current user or the entity author have the specified access right on the
80+
* specified entity, depending on the job request
81+
*/
82+
protected boolean hasAccess(Right right, EntityReference reference)
83+
{
84+
return ((!this.request.isCheckRights()
85+
|| this.authorization.hasAccess(right, this.request.getUserReference(), reference))
86+
&& (!this.request.isCheckAuthorRights()
87+
|| this.authorization.hasAccess(right, this.request.getAuthorReference(), reference)));
88+
}
89+
}

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-api/src/main/java/org/xwiki/export/pdf/internal/job/PDFExportJob.java

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.io.IOException;
2323
import java.io.InputStream;
2424
import java.net.URL;
25-
import java.util.Arrays;
2625
import java.util.List;
2726

2827
import javax.inject.Inject;
@@ -34,17 +33,11 @@
3433
import org.xwiki.export.pdf.PDFPrinter;
3534
import org.xwiki.export.pdf.internal.RequiredSkinExtensionsRecorder;
3635
import org.xwiki.export.pdf.job.PDFExportJobRequest;
37-
import org.xwiki.export.pdf.job.PDFExportJobStatus;
3836
import org.xwiki.export.pdf.job.PDFExportJobStatus.DocumentRenderingResult;
39-
import org.xwiki.job.AbstractJob;
40-
import org.xwiki.job.GroupedJob;
41-
import org.xwiki.job.JobGroupPath;
4237
import org.xwiki.model.reference.DocumentReference;
43-
import org.xwiki.model.reference.EntityReference;
4438
import org.xwiki.model.reference.ObjectPropertyReference;
4539
import org.xwiki.model.reference.ObjectReference;
4640
import org.xwiki.resource.temporary.TemporaryResourceStore;
47-
import org.xwiki.security.authorization.AuthorizationManager;
4841
import org.xwiki.security.authorization.Right;
4942

5043
/**
@@ -56,21 +49,8 @@
5649
*/
5750
@Component
5851
@Named(PDFExportJob.JOB_TYPE)
59-
public class PDFExportJob extends AbstractJob<PDFExportJobRequest, PDFExportJobStatus> implements GroupedJob
52+
public class PDFExportJob extends AbstractPDFExportJob
6053
{
61-
/**
62-
* The PDF export job type.
63-
*/
64-
public static final String JOB_TYPE = "export/pdf";
65-
66-
/**
67-
* Used to check access permissions.
68-
*
69-
* @see #hasAccess(Right, EntityReference)
70-
*/
71-
@Inject
72-
private AuthorizationManager authorization;
73-
7454
@Inject
7555
private DocumentRenderer documentRenderer;
7656

@@ -94,24 +74,6 @@ public class PDFExportJob extends AbstractJob<PDFExportJobRequest, PDFExportJobS
9474
@Inject
9575
private PrintPreviewURLBuilder printPreviewURLBuilder;
9676

97-
@Override
98-
public String getType()
99-
{
100-
return JOB_TYPE;
101-
}
102-
103-
@Override
104-
public JobGroupPath getGroupPath()
105-
{
106-
return new JobGroupPath(Arrays.asList("export", "pdf"));
107-
}
108-
109-
@Override
110-
protected PDFExportJobStatus createNewStatus(PDFExportJobRequest request)
111-
{
112-
return new PDFExportJobStatus(getType(), request, this.observationManager, this.loggerManager);
113-
}
114-
11577
@Override
11678
protected void runInternal() throws Exception
11779
{
@@ -195,22 +157,6 @@ private int render(DocumentReference documentReference, DocumentRendererParamete
195157
return renderingResult.getHTML().length();
196158
}
197159

198-
/**
199-
* Check access rights taking into account the job request.
200-
*
201-
* @param right the access right to check
202-
* @param reference the target entity reference
203-
* @return return {@code true} if the current user or the entity author have the specified access right on the
204-
* specified entity, depending on the job request
205-
*/
206-
private boolean hasAccess(Right right, EntityReference reference)
207-
{
208-
return ((!this.request.isCheckRights()
209-
|| this.authorization.hasAccess(right, this.request.getUserReference(), reference))
210-
&& (!this.request.isCheckAuthorRights()
211-
|| this.authorization.hasAccess(right, this.request.getAuthorReference(), reference)));
212-
}
213-
214160
private void saveAsPDF() throws IOException
215161
{
216162
URL printPreviewURL = this.printPreviewURLBuilder.getPrintPreviewURL(this.request);

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-test/xwiki-platform-export-pdf-test-content/src/main/resources/PDFExportIT/Parent/Child/WebHome.xml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,15 @@
4040

4141
Content of first section.
4242

43-
image:attach:xwiki-logo.png</content>
43+
[[image:attach:xwiki-logo.png||width="100"]]
44+
45+
[[image:attach:xwiki-logo.png||width="100" class="force-server-side-resize"]]
46+
47+
{{velocity}}
48+
{{html}}
49+
&lt;img src="$doc.getAttachmentURL('xwiki-logo.png', 'download', 'height=50')" alt="XWiki Logo" /&gt;
50+
{{/html}}
51+
{{/velocity}}</content>
4452
<attachment>
4553
<filename>xwiki-logo.png</filename>
4654
<mimetype>image/png</mimetype>

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-test/xwiki-platform-export-pdf-test-docker/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@
6969
<version>${project.version}</version>
7070
<scope>runtime</scope>
7171
</dependency>
72+
<!-- Needed to verify that image server-side resize is disabled while exporting to PDF. -->
73+
<dependency>
74+
<groupId>org.xwiki.platform</groupId>
75+
<artifactId>xwiki-platform-image-processing-plugin</artifactId>
76+
<version>${project.version}</version>
77+
<scope>runtime</scope>
78+
</dependency>
7279
<!--Test only dependencies. -->
7380
<dependency>
7481
<groupId>org.xwiki.platform</groupId>

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-test/xwiki-platform-export-pdf-test-docker/src/test/it/org/xwiki/export/pdf/test/ui/AllIT.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
// Starting or stopping the Office server requires PR (for the current user, on the main wiki reference).
4747
// Enabling debug logs also requires PR.
4848
"xwikiPropertiesAdditionalProperties=test.prchecker.excludePattern="
49-
+ ".*:(XWiki\\.OfficeImporterAdmin|PDFExportIT\\.EnableDebugLogs)"
49+
+ ".*:(XWiki\\.OfficeImporterAdmin|PDFExportIT\\.EnableDebugLogs)",
50+
"xwikiCfgPlugins=com.xpn.xwiki.plugin.image.ImagePlugin",
5051
}
5152
)
5253
@ExtendWith(PDFExportExecutionCondition.class)

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-test/xwiki-platform-export-pdf-test-docker/src/test/it/org/xwiki/export/pdf/test/ui/PDFExportIT.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
// Starting or stopping the Office server requires PR (for the current user, on the main wiki reference).
8282
// Enabling debug logs also requires PR.
8383
"xwikiPropertiesAdditionalProperties=test.prchecker.excludePattern="
84-
+ ".*:(XWiki\\.OfficeImporterAdmin|PDFExportIT\\.EnableDebugLogs)"
84+
+ ".*:(XWiki\\.OfficeImporterAdmin|PDFExportIT\\.EnableDebugLogs)",
85+
"xwikiCfgPlugins=com.xpn.xwiki.plugin.image.ImagePlugin",
8586
}
8687
)
8788
@ExtendWith(PDFExportExecutionCondition.class)
@@ -225,11 +226,20 @@ void exportAsPDF(TestUtils setup, TestConfiguration testConfiguration) throws Ex
225226
assertTrue(contentPageText.contains("Child\nSection 1\nContent of first section.\n"),
226227
"Child document content missing: " + contentPageText);
227228

228-
// The content of the child document shows an image.
229+
// The content of the child document shows the same image multiple times.
229230
List<PDFImage> contentPageImages = pdf.getImagesFromPage(3);
230-
assertEquals(1, contentPageImages.size());
231+
assertEquals(3, contentPageImages.size());
232+
233+
// Verify the images included in the PDF are not resized server-side (we know the image width is specified
234+
// in the source wiki syntax and we enabled the server-side image resize by default).
231235
assertEquals(512, contentPageImages.get(0).getRawWidth());
232236
assertEquals(512, contentPageImages.get(0).getRawHeight());
237+
assertEquals(512, contentPageImages.get(2).getRawWidth());
238+
assertEquals(512, contentPageImages.get(2).getRawHeight());
239+
240+
// For the second image we force the server-side resize.
241+
assertEquals(100, contentPageImages.get(1).getRawWidth());
242+
assertEquals(100, contentPageImages.get(1).getRawHeight());
233243
}
234244
}
235245

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-ui/src/main/resources/XWiki/PDFExport/Sheet.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,38 @@ require([
822822
}).wrap("&lt;span&gt;&lt;/span&gt;");
823823
};
824824

825+
/**
826+
* Removes the image size (width and height) from the image URL query string in order to prevent the server-side
827+
* resize of the image, thus allowing the full size image to be included in the generated PDF.
828+
*/
829+
async function disableServerSideImageResize() {
830+
const imageLoadPromises = []
831+
document.querySelectorAll('#xwikicontent img:not(.force-server-side-resize)').forEach(image =&gt; {
832+
const imageURL = new URL(image.src);
833+
const imageParams = imageURL.searchParams;
834+
if (imageParams.has('width') || imageParams.has('height')) {
835+
// Backup the original query string before we modify the image URL.
836+
const oldImageQueryString = imageURL.search;
837+
// The image size is specified in the URL which may trigger a server-side resize. We want the full image to be
838+
// included in the PDF so let's remove these parameters.
839+
imageParams.delete('width');
840+
imageParams.delete('height');
841+
const newImageQueryString = '?' + imageParams.toString();
842+
const newImageSrc = image.getAttribute('src').replace(oldImageQueryString, newImageQueryString);
843+
// Create a promise for when the full size image is loaded.
844+
imageLoadPromises.push(new Promise((resolve, reject) =&gt; {
845+
image.addEventListener('load', resolve);
846+
image.addEventListener('error', reject);
847+
image.addEventListener('abort', resolve);
848+
image.setAttribute('src', newImageSrc);
849+
}));
850+
}
851+
});
852+
await Promise.allSettled(imageLoadPromises);
853+
}
854+
855+
pageReady.delayPageReady(disableServerSideImageResize(), 'Disable server-side image resize.');
856+
825857
// Adjust the exported content before performing the print layout.
826858
pageReady.afterPageReady(() =&gt; {
827859
refactorAnchors();

xwiki-platform-core/xwiki-platform-export/xwiki-platform-export-pdf/xwiki-platform-export-pdf-ui/src/main/resources/XWiki/PDFExport/Translations.ko.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,4 @@ XWiki.PDFExport.ConfigurationClass_maxContentSize.hint=단일 PDF 내보내기
110110
XWiki.PDFExport.ConfigurationClass_replaceFOP=FOP 교체
111111
XWiki.PDFExport.ConfigurationClass_replaceFOP.hint=Apache Formatting Objects Processor(FOP)를 기반으로 이전 PDF 내보내기를 대체합니다.
112112
</content>
113-
</xwikidoc>
113+
</xwikidoc>

0 commit comments

Comments
 (0)