|
9 | 9 | import java.io.File; |
10 | 10 | import java.io.IOException; |
11 | 11 | import java.util.ArrayList; |
| 12 | +import java.util.Arrays; |
12 | 13 | import java.util.List; |
| 14 | +import java.util.Locale; |
| 15 | +import java.util.Random; |
13 | 16 |
|
14 | 17 | import org.apache.commons.io.FileUtils; |
15 | 18 | import org.apache.pdfbox.io.IOUtils; |
|
29 | 32 | import org.apache.pdfbox.util.Charsets; |
30 | 33 | import org.hamcrest.CustomTypeSafeMatcher; |
31 | 34 | import org.junit.Assert; |
32 | | -import org.junit.Ignore; |
33 | 35 | import org.junit.Test; |
34 | 36 |
|
35 | 37 | import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; |
36 | 38 | import com.openhtmltopdf.testcases.TestcaseRunner; |
| 39 | +import com.openhtmltopdf.visualregressiontests.VisualRegressionTest; |
37 | 40 | import com.openhtmltopdf.visualtest.TestSupport; |
38 | 41 | import com.openhtmltopdf.visualtest.VisualTester.BuilderConfig; |
39 | 42 |
|
@@ -821,6 +824,143 @@ public void testPR480LinkShapes() throws IOException { |
821 | 824 | } |
822 | 825 | } |
823 | 826 |
|
| 827 | + /** |
| 828 | + * Runs a fuzz test, optionally with PDFBOX included font with non-zero-width |
| 829 | + * soft hyphen. |
| 830 | + */ |
| 831 | + private static void runFuzzTest(String html, boolean useFont) throws IOException { |
| 832 | + final String header = useFont ? |
| 833 | + "<html><body style=\"font-family: 'Liberation Sans'\">" : |
| 834 | + "<html><body>"; |
| 835 | + final String footer = "</body></html>"; |
| 836 | + |
| 837 | + System.out.println("The test is " + html.length() + " chars long."); |
| 838 | + |
| 839 | + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { |
| 840 | + PdfRendererBuilder builder = new PdfRendererBuilder(); |
| 841 | + builder.useFastMode(); |
| 842 | + builder.toStream(os); |
| 843 | + builder.withHtmlContent(header + html + footer, null); |
| 844 | + if (useFont) { |
| 845 | + builder.useFont(() -> VisualRegressionTest.class.getClassLoader().getResourceAsStream("org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"), |
| 846 | + "Liberation Sans"); |
| 847 | + } |
| 848 | + builder.run(); |
| 849 | + |
| 850 | + // Files.write(Paths.get("./target/html.txt"), html.getBytes(StandardCharsets.UTF_8)); |
| 851 | + // Files.write(Paths.get("./target/pdf.pdf"), os.toByteArray()); |
| 852 | + |
| 853 | + System.out.println("The result is " + os.size() + " bytes long."); |
| 854 | + } |
| 855 | + } |
| 856 | + |
| 857 | + /** |
| 858 | + * Creates a fuzz test with random characters given the styles provided in |
| 859 | + * arguments. |
| 860 | + */ |
| 861 | + private static void createCombinationTest( |
| 862 | + StringBuilder sb, int widthPx, String whiteSpace, String wordWrap, List<char[]> all, Random rndm, int testCharCount) { |
| 863 | + String start = String.format(Locale.US, "<div style=\"white-space: %s; word-wrap: %s; width: %dpx;\">", |
| 864 | + whiteSpace, wordWrap, widthPx); |
| 865 | + String end = "</div>"; |
| 866 | + |
| 867 | + sb.append(start); |
| 868 | + |
| 869 | + int len = 0; |
| 870 | + while (true) { |
| 871 | + char[] charCombi = all.get(rndm.nextInt(all.size())); |
| 872 | + |
| 873 | + if (len + charCombi.length > testCharCount) { |
| 874 | + String combi = String.valueOf(charCombi); |
| 875 | + sb.append(combi.substring(0, testCharCount - len)); |
| 876 | + break; |
| 877 | + } |
| 878 | + |
| 879 | + sb.append(charCombi); |
| 880 | + len += charCombi.length; |
| 881 | + } |
| 882 | + |
| 883 | + sb.append(end); |
| 884 | + } |
| 885 | + |
| 886 | + /** |
| 887 | + * Creates all 5 character combinations from a list of characters |
| 888 | + * which have special meaning to the line breaking algorithms. |
| 889 | + */ |
| 890 | + private static List<char[]> createAllCombinations() { |
| 891 | + char[] chars = new char[] { 'x', '\u00ad', '\n', '\r', ' ' }; |
| 892 | + int[] loopIndices = new int[chars.length]; |
| 893 | + int totalCombinations = (int) Math.pow(loopIndices.length, loopIndices.length); |
| 894 | + |
| 895 | + List<char[]> ret = new ArrayList<>(totalCombinations); |
| 896 | + |
| 897 | + for (int i = 0; i < totalCombinations; i++) { |
| 898 | + char[] result = new char[loopIndices.length]; |
| 899 | + |
| 900 | + for (int k = 0; k < loopIndices.length; k++) { |
| 901 | + char ch = chars[loopIndices[k]]; |
| 902 | + result[k] = ch; |
| 903 | + } |
| 904 | + |
| 905 | + ret.add(result); |
| 906 | + |
| 907 | + boolean carry = true; |
| 908 | + for (int j = loopIndices.length - 1; j >= 0; j--) { |
| 909 | + if (carry) { |
| 910 | + loopIndices[j]++; |
| 911 | + carry = false; |
| 912 | + } |
| 913 | + |
| 914 | + if (loopIndices[j] >= chars.length) { |
| 915 | + loopIndices[j] = 0; |
| 916 | + carry = true; |
| 917 | + } |
| 918 | + } |
| 919 | + } |
| 920 | + |
| 921 | + return ret; |
| 922 | + } |
| 923 | + |
| 924 | + /** |
| 925 | + * Tests the line breaking algorithms against infinite loop bugs |
| 926 | + * by using many combinations of styles and random character sequences. |
| 927 | + */ |
| 928 | + @Test |
| 929 | + public void testPr492InfiniteLoopBugsInLineBreakingFuzz() throws IOException { |
| 930 | + final String[] whiteSpace = new String[] { "normal", "pre", "nowrap", "pre-wrap", "pre-line" }; |
| 931 | + final String[] wordWrap = new String[] { "normal", "break-word" }; |
| 932 | + final List<char[]> all = createAllCombinations(); |
| 933 | + final Random rndm = new Random(); |
| 934 | + long seed = rndm.nextLong(); |
| 935 | + |
| 936 | + System.out.println("For NonVisualRegressionTest::testPr492InfiniteLoopBugsInLineBreakingFuzz " + |
| 937 | + "using a random seed of " + seed + " for Random instance."); |
| 938 | + rndm.setSeed(seed); |
| 939 | + |
| 940 | + List<Integer> lengths = new ArrayList<>(); |
| 941 | + lengths.addAll(Arrays.asList(0, 1, 2, 3, 37, 79)); |
| 942 | + |
| 943 | + for (int i = 0; i < 4; i++) { |
| 944 | + lengths.add(rndm.nextInt(150)); |
| 945 | + } |
| 946 | + |
| 947 | + StringBuilder sb = new StringBuilder(); |
| 948 | + |
| 949 | + for (int i = 0; i < 100; i++) { |
| 950 | + for (int j = 0; j < whiteSpace.length; j++) { |
| 951 | + for (int k = 0; k < wordWrap.length; k++) { |
| 952 | + for (Integer len : lengths) { |
| 953 | + createCombinationTest(sb, i, whiteSpace[j], wordWrap[k], all, rndm, len); |
| 954 | + } |
| 955 | + } |
| 956 | + } |
| 957 | + } |
| 958 | + |
| 959 | + runFuzzTest(sb.toString(), false); |
| 960 | + runFuzzTest(sb.toString(), true); |
| 961 | + } |
| 962 | + |
| 963 | + |
824 | 964 | // TODO: |
825 | 965 | // + More form controls. |
826 | 966 | // + Custom meta info. |
|
0 commit comments