|
| 1 | +package com.demcha.examples.features.svg; |
| 2 | + |
| 3 | +import com.demcha.compose.GraphCompose; |
| 4 | +import com.demcha.compose.document.api.DocumentSession; |
| 5 | +import com.demcha.compose.document.dsl.SectionBuilder; |
| 6 | +import com.demcha.compose.document.node.DocumentNode; |
| 7 | +import com.demcha.compose.document.node.TextAlign; |
| 8 | +import com.demcha.compose.document.style.DocumentColor; |
| 9 | +import com.demcha.compose.document.style.DocumentInsets; |
| 10 | +import com.demcha.compose.document.style.DocumentTextStyle; |
| 11 | +import com.demcha.compose.document.svg.SvgIcon; |
| 12 | +import com.demcha.examples.support.ExampleOutputPaths; |
| 13 | + |
| 14 | +import java.io.InputStream; |
| 15 | +import java.nio.charset.StandardCharsets; |
| 16 | +import java.nio.file.Path; |
| 17 | +import java.util.List; |
| 18 | +import java.util.Locale; |
| 19 | +import java.util.Objects; |
| 20 | + |
| 21 | +/** |
| 22 | + * Runnable stress-test gallery for the beta SVG icon reader: 34 real-world |
| 23 | + * multicolour icons (up to 19 layers each) read straight from {@code .svg} |
| 24 | + * resources via {@link SvgIcon#parse(String)} and stacked on the page with |
| 25 | + * {@code addSvgIcon(...)} — every curve a native PDF Bézier, the whole icon |
| 26 | + * set a fraction of one screenshot's weight. |
| 27 | + * |
| 28 | + * <pre>{@code |
| 29 | + * flow.addSvgIcon(SvgIcon.read(Path.of("icons/apple.svg")), 52); |
| 30 | + * }</pre> |
| 31 | + * |
| 32 | + * <p>Icon artwork: <a href="https://www.svgrepo.com">svgrepo.com</a> |
| 33 | + * collections (see each icon's page for its licence).</p> |
| 34 | + * |
| 35 | + * @author Artem Demchyshyn |
| 36 | + */ |
| 37 | +public final class SvgIconGalleryExample { |
| 38 | + |
| 39 | + private static final List<String> ICONS = List.of( |
| 40 | + "apple", "avocado", "banana", "boxing", "calendar", |
| 41 | + "camera-take-pictures", "chat-chat", "cherry", "diagnosis", "eye-password-eye-password", |
| 42 | + "feet", "food", "grape", "headphones-music", "key-password", |
| 43 | + "kiwi-fruit", "magnifying-glass-find-search", "microphone-singing", "movie", "peach", |
| 44 | + "pencil-revision", "personal-account-account", "picture", "record", "reminder-alert", |
| 45 | + "setting", "shopping-cart", "shopping", "social-contact", "starfish", |
| 46 | + "steak", "store-homepage-home", "toolbox", "upload"); |
| 47 | + |
| 48 | + private static final int COLUMNS = 5; |
| 49 | + private static final double ICON_SIZE = 50; |
| 50 | + |
| 51 | + private SvgIconGalleryExample() { |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + * Renders the 34-icon gallery sheet with a caption under every icon. |
| 56 | + * |
| 57 | + * @return path to the generated PDF |
| 58 | + * @throws Exception if rendering or resource IO fails |
| 59 | + */ |
| 60 | + public static Path generate() throws Exception { |
| 61 | + Path pdfFile = ExampleOutputPaths.prepare("features/svg", "svg-icon-gallery.pdf"); |
| 62 | + |
| 63 | + DocumentTextStyle caption = DocumentTextStyle.DEFAULT |
| 64 | + .withSize(7.5) |
| 65 | + .withColor(DocumentColor.rgb(90, 96, 105)); |
| 66 | + |
| 67 | + try (DocumentSession document = GraphCompose.document(pdfFile) |
| 68 | + .pageSize(595, 842) |
| 69 | + .margin(DocumentInsets.of(34)) |
| 70 | + .create()) { |
| 71 | + document.pageFlow(page -> { |
| 72 | + page.addParagraph(p -> p |
| 73 | + .text("SVG icon gallery") |
| 74 | + .textStyle(DocumentTextStyle.DEFAULT.withSize(22))); |
| 75 | + page.addParagraph(p -> p |
| 76 | + .text("34 real-world multicolour icons (svgrepo.com) read by SvgIcon.parse " |
| 77 | + + "— every layer a native vector path, the whole set 156 KB of sources.") |
| 78 | + .textStyle(DocumentTextStyle.DEFAULT.withSize(9.5) |
| 79 | + .withColor(DocumentColor.rgb(90, 96, 105))) |
| 80 | + .padding(DocumentInsets.bottom(14))); |
| 81 | + |
| 82 | + for (int start = 0; start < ICONS.size(); start += COLUMNS) { |
| 83 | + List<String> chunk = ICONS.subList(start, Math.min(start + COLUMNS, ICONS.size())); |
| 84 | + page.addRow(row -> { |
| 85 | + row.spacing(10).evenWeights().margin(DocumentInsets.bottom(12)); |
| 86 | + for (String name : chunk) { |
| 87 | + row.add(cell(name, caption)); |
| 88 | + } |
| 89 | + }); |
| 90 | + } |
| 91 | + }); |
| 92 | + |
| 93 | + document.buildPdf(); |
| 94 | + } |
| 95 | + |
| 96 | + return pdfFile; |
| 97 | + } |
| 98 | + |
| 99 | + private static DocumentNode cell(String name, DocumentTextStyle caption) { |
| 100 | + SvgIcon icon = loadIcon(name); |
| 101 | + return new SectionBuilder() |
| 102 | + .name("Icon" + name.replace('-', '_')) |
| 103 | + .spacing(4) |
| 104 | + .addSvgIcon(icon, ICON_SIZE) |
| 105 | + .addParagraph(p -> p |
| 106 | + .text(pretty(name)) |
| 107 | + .align(TextAlign.LEFT) |
| 108 | + .textStyle(caption)) |
| 109 | + .build(); |
| 110 | + } |
| 111 | + |
| 112 | + private static SvgIcon loadIcon(String name) { |
| 113 | + try (InputStream in = Objects.requireNonNull( |
| 114 | + SvgIconGalleryExample.class.getResourceAsStream("/icons/" + name + ".svg"), |
| 115 | + "icon resource missing: " + name)) { |
| 116 | + return SvgIcon.parse(new String(in.readAllBytes(), StandardCharsets.UTF_8)); |
| 117 | + } catch (Exception e) { |
| 118 | + throw new IllegalStateException("failed to load icon: " + name, e); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + /** {@code "camera-take-pictures"} → {@code "Camera take pictures"}. */ |
| 123 | + private static String pretty(String name) { |
| 124 | + String spaced = name.replace('-', ' '); |
| 125 | + return Character.toUpperCase(spaced.charAt(0)) + spaced.substring(1).toLowerCase(Locale.ROOT); |
| 126 | + } |
| 127 | + |
| 128 | + public static void main(String[] args) throws Exception { |
| 129 | + System.out.println("Generated: " + generate()); |
| 130 | + } |
| 131 | +} |
0 commit comments