Skip to content

Commit a70cf00

Browse files
author
Dmitry Radchuk
committed
Add support for rich text
DEVSIX-9512
1 parent 9332bb1 commit a70cf00

File tree

9 files changed

+644
-34
lines changed

9 files changed

+644
-34
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/PageResizer.java

Lines changed: 391 additions & 34 deletions
Large diffs are not rendered by default.

kernel/src/test/java/com/itextpdf/kernel/pdf/PageResizerTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,32 @@ public void testAnnotationCalloutLine() throws IOException, InterruptedException
155155
SOURCE_FOLDER + "cmp_" + outFileName, DESTINATION_FOLDER, "diff"));
156156
}
157157

158+
@Test
159+
public void testAnnotationRichText() throws IOException, InterruptedException {
160+
String inFileName = "annotationRichText.pdf";
161+
String outFileName = "annotationRichText.pdf";
162+
String outFileNameReverted = "annotationRichTextReverted.pdf";
163+
164+
try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(SOURCE_FOLDER + inFileName),
165+
new PdfWriter(DESTINATION_FOLDER + outFileName))) {
166+
new PageResizer(new PageSize(PageSize.A4.getWidth()/2,PageSize.A4.getHeight()),
167+
PageResizer.ResizeType.MAINTAIN_ASPECT_RATIO).resize(pdfDocument.getPage(1));
168+
}
169+
Assertions.assertNull(new CompareTool()
170+
.compareByContent(DESTINATION_FOLDER + outFileName,
171+
SOURCE_FOLDER + "cmp_" + outFileName, DESTINATION_FOLDER, "diff"));
172+
173+
// Reverting
174+
try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(SOURCE_FOLDER + outFileName),
175+
new PdfWriter(DESTINATION_FOLDER + outFileNameReverted))) {
176+
PageResizer resizer = new PageResizer(new PageSize(PageSize.A4), ResizeType.MAINTAIN_ASPECT_RATIO);
177+
resizer.resize(pdfDocument.getPage(1));
178+
}
179+
Assertions.assertNull(new CompareTool()
180+
.compareByContent(DESTINATION_FOLDER + outFileNameReverted,
181+
SOURCE_FOLDER + "cmp_" + outFileNameReverted, DESTINATION_FOLDER, "diff"));
182+
}
183+
158184
@Test
159185
public void testAnnotationInkList() throws IOException, InterruptedException {
160186
String inFileName = "annotationInkList.pdf";

kernel/src/test/java/com/itextpdf/kernel/pdf/PageResizerUnitTest.java

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,231 @@ public void resizePageWithZeroSizeTest() {
194194
.CANNOT_RESIZE_PAGE_WITH_NEGATIVE_OR_INFINITE_SCALE, new PageSize(0.0F, 0.0F));
195195
Assertions.assertEquals(expectedMessage, exception.getMessage());
196196
}
197+
198+
@Test
199+
public void scaleRcStringWithNullOrEmptyReturnsAsIs() {
200+
Assertions.assertNull(PageResizer.scaleRcString(null, 2.0));
201+
Assertions.assertEquals("", PageResizer.scaleRcString("", 2.0));
202+
Assertions.assertEquals(" ", PageResizer.scaleRcString(" ", 2.0));
203+
}
204+
205+
@Test
206+
public void scaleRcStringWithIdentityScaleReturnsAsIs() {
207+
String input = "font-size: 12.5pt; width: 100px;";
208+
Assertions.assertEquals(input, PageResizer.scaleRcString(input, 1.0));
209+
}
210+
211+
@Test
212+
public void scaleRcStringWithNonScalablePropertiesReturnsAsIs() {
213+
String input = "color: red; font-weight: bold;";
214+
Assertions.assertEquals(input, PageResizer.scaleRcString(input, 2.0));
215+
String input2 = "margin: 10pt 5pt; padding: 10px;";
216+
Assertions.assertEquals(input2, PageResizer.scaleRcString(input2, 2.0), "Shorthand properties should be ignored.");
217+
}
218+
219+
@Test
220+
public void scaleRcStringScalesValuesWithAbsoluteUnits() {
221+
String input = "font-size: 12pt;";
222+
String expected = "font-size: 6pt;";
223+
Assertions.assertEquals(expected, PageResizer.scaleRcString(input, 0.5));
224+
225+
String multiInput = "width: 1in; height: 2pc; margin-left: 3cm; margin-top: 4mm; border-top-width: 5px;";
226+
String multiExpected = "width: 0.5in; height: 1pc; margin-left: 1.5cm; margin-top: 2mm; border-top-width: 2.5px;";
227+
Assertions.assertEquals(multiExpected, PageResizer.scaleRcString(multiInput, 0.5));
228+
}
229+
230+
@Test
231+
public void scaleRcStringWithRelativeOrNoUnitsReturnsAsIs() {
232+
String input = "font-size: 1.5em; width: 100%; line-height: 150%;";
233+
Assertions.assertEquals(input, PageResizer.scaleRcString(input, 2.0));
234+
String noUnitInput = "font-size: 12";
235+
Assertions.assertEquals(noUnitInput, PageResizer.scaleRcString(noUnitInput, 2.0));
236+
}
237+
238+
@Test
239+
public void scaleRcStringHandlesVariousNumberFormats() {
240+
Assertions.assertEquals("font-size: 20pt;", PageResizer.scaleRcString("font-size: 10pt;", 2.0), "Integer");
241+
Assertions.assertEquals("font-size: 21pt;", PageResizer.scaleRcString("font-size: 10.5pt;", 2.0), "Decimal");
242+
Assertions.assertEquals("font-size: 1pt;", PageResizer.scaleRcString("font-size: .5pt;", 2.0), "Leading dot decimal");
243+
Assertions.assertEquals("width: 50px;", PageResizer.scaleRcString("width: 1e2px;", 0.5), "Scientific notation");
244+
Assertions.assertEquals("margin-left: -20px;", PageResizer.scaleRcString("margin-left: -10px;", 2.0), "Negative value");
245+
}
246+
247+
@Test
248+
public void scaleRcStringHandlesVariedWhitespace() {
249+
String input = "font-size: 12pt ; width:50px";
250+
String expected = "font-size: 24pt ; width:100px";
251+
Assertions.assertEquals(expected, PageResizer.scaleRcString(input, 2.0));
252+
}
253+
254+
@Test
255+
public void scaleRcStringWithZeroScale_scalesToZero() {
256+
String input = "font-size: 12pt; width: 100px;";
257+
String expected = "font-size: 0pt; width: 0px;";
258+
Assertions.assertEquals(expected, PageResizer.scaleRcString(input, 0.0));
259+
}
260+
261+
@Test
262+
public void scaleRcStringHandlesSmallResultingValues() {
263+
Assertions.assertEquals("font-size: 0.0001pt;", PageResizer.scaleRcString("font-size: 0.001pt;", 0.1));
264+
Assertions.assertEquals("font-size: 0pt;", PageResizer.scaleRcString("font-size: 0.001pt;", 0.01));
265+
}
266+
267+
@Test
268+
public void scaleRcStringWithComplexRichTextScalesCorrectly() {
269+
String rc = "body{font-family:helvetica,sans-serif;font-size:12.0pt;line-height:14.0pt;margin-top:2.0mm;"
270+
+ "margin-bottom:2.0mm;text-align:left;}p{margin-top:0.0pt;margin-bottom:0.0pt;}";
271+
String expected = "body{font-family:helvetica,sans-serif;font-size:18pt;line-height:21pt;margin-top:3mm;"
272+
+ "margin-bottom:3mm;text-align:left;}p{margin-top:0pt;margin-bottom:0pt;}";
273+
Assertions.assertEquals(expected, PageResizer.scaleRcString(rc, 1.5));
274+
}
275+
276+
@Test
277+
public void scaleRcStringDoesNotScaleRelativeUnitsOnScalableProperties() {
278+
// em, rem, % should not be scaled
279+
String input = "font-size: 1.5em; line-height: 150%; letter-spacing: 0.5rem;";
280+
Assertions.assertEquals(input, PageResizer.scaleRcString(input, 2.0));
281+
}
282+
283+
@Test
284+
public void scaleRcStringPreservesWhitespaceBetweenNumberAndUnit() {
285+
String input = "font-size: 12 pt; width: 10 px; height: 5 cm;";
286+
String expected = "font-size: 24 pt; width: 20 px; height: 10 cm;";
287+
Assertions.assertEquals(expected, PageResizer.scaleRcString(input, 2.0));
288+
}
289+
290+
@Test
291+
public void scaleRcStringPropertyNameCaseSensitivity_notScaled() {
292+
// CSS in XHTML is case-sensitive here; property name with different case should not match
293+
String input = "Font-Size: 12pt; WIDTH: 100px;";
294+
Assertions.assertEquals(input, PageResizer.scaleRcString(input, 2.0));
295+
}
296+
297+
@Test
298+
public void scaleRcStringIgnoresShorthandBorderWidth() {
299+
String input = "border: 5px;";
300+
// Shorthand property is not in the scalable list, should remain unchanged
301+
Assertions.assertEquals(input, PageResizer.scaleRcString(input, 3.0));
302+
}
303+
304+
@Test
305+
public void scaleRcStringScientificNotationNegativeExponent() {
306+
Assertions.assertEquals("width: 0.1in;", PageResizer.scaleRcString("width: 1e-2in;", 10.0));
307+
}
308+
309+
@Test
310+
public void scaleRcStringMixedAbsoluteAndRelativeValues() {
311+
String input = "font-size: 10pt; line-height: 120%; width: 2cm;";
312+
String expected = "font-size: 20pt; line-height: 120%; width: 4cm;";
313+
Assertions.assertEquals(expected, PageResizer.scaleRcString(input, 2.0));
314+
}
315+
316+
@Test
317+
public void scaleRcAcrobatExample() {
318+
String input = "<?xml version=\"1.0\"?><body xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\" xfa:spec=\"2.0.2\"><p style=\"font-size:12pt;padding-left:5pt;padding-top:2pt;margin-bottom:6pt;line-height:15pt;text-align:left;color:#000000;\">This is the first paragraph. It has a specific<span style=\"font-weight:bold; color:#FF0000;\">font size and line height.</span></p><p style=\"font-size:12pt;margin-top:0pt;text-indent:24pt;color:#333333;\">This is the second paragraph. Notice the <b>text-indent</b> property, which creates the indentation.</p></body>";
319+
String expected = "<?xml version=\"1.0\"?><body xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\" xfa:spec=\"2.0.2\"><p style=\"font-size:6pt;padding-left:2.5pt;padding-top:1pt;margin-bottom:3pt;line-height:7.5pt;text-align:left;color:#000000;\">This is the first paragraph. It has a specific<span style=\"font-weight:bold; color:#FF0000;\">font size and line height.</span></p><p style=\"font-size:6pt;margin-top:0pt;text-indent:12pt;color:#333333;\">This is the second paragraph. Notice the <b>text-indent</b> property, which creates the indentation.</p></body>";
320+
Assertions.assertEquals(expected, PageResizer.scaleRcString(input, 0.5));
321+
}
322+
323+
@Test
324+
public void scaleRcStringPreservesWhitespaceBetweenPropertyAndValue() {
325+
// Test various whitespace patterns are preserved
326+
String input1 = "font-size: 12pt;";
327+
String expected1 = "font-size: 24pt;";
328+
Assertions.assertEquals(expected1, PageResizer.scaleRcString(input1, 2.0),
329+
"Multiple spaces after colon should be preserved");
330+
331+
String input2 = "font-size:\t10pt;";
332+
String expected2 = "font-size:\t20pt;";
333+
Assertions.assertEquals(expected2, PageResizer.scaleRcString(input2, 2.0),
334+
"Tab after colon should be preserved");
335+
336+
String input3 = "font-size: 10pt; width: 20px;";
337+
String expected3 = "font-size: 20pt; width: 40px;";
338+
Assertions.assertEquals(expected3, PageResizer.scaleRcString(input3, 2.0),
339+
"Different whitespace patterns should be preserved independently");
340+
341+
String input4 = "font-size:10pt;";
342+
String expected4 = "font-size:20pt;";
343+
Assertions.assertEquals(expected4, PageResizer.scaleRcString(input4, 2.0),
344+
"No space after colon should be preserved");
345+
346+
String input5 = "margin-left: 5 pt;";
347+
String expected5 = "margin-left: 10 pt;";
348+
Assertions.assertEquals(expected5, PageResizer.scaleRcString(input5, 2.0),
349+
"Space between number and unit should be preserved");
350+
}
351+
352+
@Test
353+
public void scaleRcStringWordBoundaryCheck() {
354+
String input1 = "font-size-large: 10pt;";
355+
Assertions.assertEquals(input1, PageResizer.scaleRcString(input1, 2.0),
356+
"Property 'font-size-large' should not be treated as 'font-size'");
357+
358+
String input2 = "width2: 20px;";
359+
Assertions.assertEquals(input2, PageResizer.scaleRcString(input2, 2.0),
360+
"Property 'width2' should not be treated as 'width'");
361+
362+
String input3 = "my-height: 30pt;";
363+
Assertions.assertEquals(input3, PageResizer.scaleRcString(input3, 2.0),
364+
"Property 'my-height' should not be treated as 'height'");
365+
366+
String input4 = "margin-top-extra: 15px;";
367+
Assertions.assertEquals(input4, PageResizer.scaleRcString(input4, 2.0),
368+
"Property 'margin-top-extra' should not be treated as 'margin-top'");
369+
370+
String input5 = "font-size: 12pt; custom-font-size: 10pt;";
371+
String expected5 = "font-size: 24pt; custom-font-size: 10pt;";
372+
Assertions.assertEquals(expected5, PageResizer.scaleRcString(input5, 2.0),
373+
"Valid 'font-size' should be scaled, but 'custom-font-size' should not");
374+
375+
String input6 = "width: 100px";
376+
String expected6 = "width: 200px";
377+
Assertions.assertEquals(expected6, PageResizer.scaleRcString(input6, 2.0),
378+
"Property 'width' at end of string should be scaled");
379+
380+
String input7 = "font-size: 10pt; font-size-custom: 20pt; height: 50px; height123: 30px;";
381+
String expected7 = "font-size: 20pt; font-size-custom: 20pt; height: 100px; height123: 30px;";
382+
Assertions.assertEquals(expected7, PageResizer.scaleRcString(input7, 2.0),
383+
"Only exact property name matches should be scaled");
384+
}
385+
386+
@Test
387+
public void scaleRcStringWithMalformedNumberReturnsAsIs() {
388+
// A single dot is not a valid number.
389+
String input1 = "font-size: .pt;";
390+
Assertions.assertEquals(input1, PageResizer.scaleRcString(input1, 2.0));
391+
392+
// A single sign is not a valid number.
393+
String input2 = "font-size: +pt;";
394+
Assertions.assertEquals(input2, PageResizer.scaleRcString(input2, 2.0));
395+
396+
// A dot after a number and sign is not valid.
397+
String input3 = "font-size: -.pt;";
398+
Assertions.assertEquals(input3, PageResizer.scaleRcString(input3, 2.0));
399+
400+
// Exponent without mantissa
401+
String input4 = "font-size: E1pt;";
402+
Assertions.assertEquals(input4, PageResizer.scaleRcString(input4, 2.0));
403+
404+
// Number with exponent but no digits in exponent
405+
String input5 = "font-size: 1Ept;";
406+
Assertions.assertEquals(input5, PageResizer.scaleRcString(input5, 2.0));
407+
}
408+
409+
@Test
410+
public void scaleRcStringIgnoresComments() {
411+
String htmlComment = "font-size: 20pt; <!-- font-size: 10pt; -->";
412+
String expectedHtml = "font-size: 40pt; <!-- font-size: 20pt; -->";
413+
Assertions.assertEquals(expectedHtml, PageResizer.scaleRcString(htmlComment, 2.0));
414+
415+
String blockComment = "font-size: 20pt; /** font-size: 10pt; **/";
416+
String expectedBlock = "font-size: 40pt; /** font-size: 20pt; **/";
417+
Assertions.assertEquals(expectedBlock, PageResizer.scaleRcString(blockComment, 2.0));
418+
419+
String combined = "<!-- width: 50px; --> width: 100px; /** height: 40pt; */ height: 80pt;";
420+
String expectedCombined = "<!-- width: 100px; --> width: 200px; /** height: 80pt; */ height: 160pt;";
421+
Assertions.assertEquals(expectedCombined, PageResizer.scaleRcString(combined, 2.0));
422+
}
423+
197424
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)