15317bbafSopenharmony_ci/* 25317bbafSopenharmony_ci * QR Code generator demo (Java) 35317bbafSopenharmony_ci * 45317bbafSopenharmony_ci * Run this command-line program with no arguments. The program creates/overwrites a bunch of 55317bbafSopenharmony_ci * PNG and SVG files in the current working directory to demonstrate the creation of QR Codes. 65317bbafSopenharmony_ci * 75317bbafSopenharmony_ci * Copyright (c) Project Nayuki. (MIT License) 85317bbafSopenharmony_ci * https://www.nayuki.io/page/qr-code-generator-library 95317bbafSopenharmony_ci * 105317bbafSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy of 115317bbafSopenharmony_ci * this software and associated documentation files (the "Software"), to deal in 125317bbafSopenharmony_ci * the Software without restriction, including without limitation the rights to 135317bbafSopenharmony_ci * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 145317bbafSopenharmony_ci * the Software, and to permit persons to whom the Software is furnished to do so, 155317bbafSopenharmony_ci * subject to the following conditions: 165317bbafSopenharmony_ci * - The above copyright notice and this permission notice shall be included in 175317bbafSopenharmony_ci * all copies or substantial portions of the Software. 185317bbafSopenharmony_ci * - The Software is provided "as is", without warranty of any kind, express or 195317bbafSopenharmony_ci * implied, including but not limited to the warranties of merchantability, 205317bbafSopenharmony_ci * fitness for a particular purpose and noninfringement. In no event shall the 215317bbafSopenharmony_ci * authors or copyright holders be liable for any claim, damages or other 225317bbafSopenharmony_ci * liability, whether in an action of contract, tort or otherwise, arising from, 235317bbafSopenharmony_ci * out of or in connection with the Software or the use or other dealings in the 245317bbafSopenharmony_ci * Software. 255317bbafSopenharmony_ci */ 265317bbafSopenharmony_ci 275317bbafSopenharmony_ciimport java.awt.image.BufferedImage; 285317bbafSopenharmony_ciimport java.io.File; 295317bbafSopenharmony_ciimport java.io.IOException; 305317bbafSopenharmony_ciimport java.nio.charset.StandardCharsets; 315317bbafSopenharmony_ciimport java.nio.file.Files; 325317bbafSopenharmony_ciimport java.util.Arrays; 335317bbafSopenharmony_ciimport java.util.List; 345317bbafSopenharmony_ciimport java.util.Objects; 355317bbafSopenharmony_ciimport javax.imageio.ImageIO; 365317bbafSopenharmony_ciimport io.nayuki.qrcodegen.QrCode; 375317bbafSopenharmony_ciimport io.nayuki.qrcodegen.QrSegment; 385317bbafSopenharmony_ciimport io.nayuki.qrcodegen.QrSegmentAdvanced; 395317bbafSopenharmony_ci 405317bbafSopenharmony_ci 415317bbafSopenharmony_cipublic final class QrCodeGeneratorDemo { 425317bbafSopenharmony_ci 435317bbafSopenharmony_ci // The main application program. 445317bbafSopenharmony_ci public static void main(String[] args) throws IOException { 455317bbafSopenharmony_ci doBasicDemo(); 465317bbafSopenharmony_ci doVarietyDemo(); 475317bbafSopenharmony_ci doSegmentDemo(); 485317bbafSopenharmony_ci doMaskDemo(); 495317bbafSopenharmony_ci } 505317bbafSopenharmony_ci 515317bbafSopenharmony_ci 525317bbafSopenharmony_ci 535317bbafSopenharmony_ci /*---- Demo suite ----*/ 545317bbafSopenharmony_ci 555317bbafSopenharmony_ci // Creates a single QR Code, then writes it to a PNG file and an SVG file. 565317bbafSopenharmony_ci private static void doBasicDemo() throws IOException { 575317bbafSopenharmony_ci String text = "Hello, world!"; // User-supplied Unicode text 585317bbafSopenharmony_ci QrCode.Ecc errCorLvl = QrCode.Ecc.LOW; // Error correction level 595317bbafSopenharmony_ci 605317bbafSopenharmony_ci QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol 615317bbafSopenharmony_ci 625317bbafSopenharmony_ci BufferedImage img = toImage(qr, 10, 4); // Convert to bitmap image 635317bbafSopenharmony_ci File imgFile = new File("hello-world-QR.png"); // File path for output 645317bbafSopenharmony_ci ImageIO.write(img, "png", imgFile); // Write image to file 655317bbafSopenharmony_ci 665317bbafSopenharmony_ci String svg = toSvgString(qr, 4, "#FFFFFF", "#000000"); // Convert to SVG XML code 675317bbafSopenharmony_ci File svgFile = new File("hello-world-QR.svg"); // File path for output 685317bbafSopenharmony_ci Files.write(svgFile.toPath(), // Write image to file 695317bbafSopenharmony_ci svg.getBytes(StandardCharsets.UTF_8)); 705317bbafSopenharmony_ci } 715317bbafSopenharmony_ci 725317bbafSopenharmony_ci 735317bbafSopenharmony_ci // Creates a variety of QR Codes that exercise different features of the library, and writes each one to file. 745317bbafSopenharmony_ci private static void doVarietyDemo() throws IOException { 755317bbafSopenharmony_ci QrCode qr; 765317bbafSopenharmony_ci 775317bbafSopenharmony_ci // Numeric mode encoding (3.33 bits per digit) 785317bbafSopenharmony_ci qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); 795317bbafSopenharmony_ci writePng(toImage(qr, 13, 1), "pi-digits-QR.png"); 805317bbafSopenharmony_ci 815317bbafSopenharmony_ci // Alphanumeric mode encoding (5.5 bits per character) 825317bbafSopenharmony_ci qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); 835317bbafSopenharmony_ci writePng(toImage(qr, 10, 2), "alphanumeric-QR.png"); 845317bbafSopenharmony_ci 855317bbafSopenharmony_ci // Unicode text as UTF-8 865317bbafSopenharmony_ci qr = QrCode.encodeText("こんにちwa、世界! αβγδ", QrCode.Ecc.QUARTILE); 875317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "unicode-QR.png"); 885317bbafSopenharmony_ci 895317bbafSopenharmony_ci // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) 905317bbafSopenharmony_ci qr = QrCode.encodeText( 915317bbafSopenharmony_ci "Alice was beginning to get very tired of sitting by her sister on the bank, " 925317bbafSopenharmony_ci + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " 935317bbafSopenharmony_ci + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " 945317bbafSopenharmony_ci + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " 955317bbafSopenharmony_ci + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " 965317bbafSopenharmony_ci + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " 975317bbafSopenharmony_ci + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH); 985317bbafSopenharmony_ci writePng(toImage(qr, 6, 10), "alice-wonderland-QR.png"); 995317bbafSopenharmony_ci } 1005317bbafSopenharmony_ci 1015317bbafSopenharmony_ci 1025317bbafSopenharmony_ci // Creates QR Codes with manually specified segments for better compactness. 1035317bbafSopenharmony_ci private static void doSegmentDemo() throws IOException { 1045317bbafSopenharmony_ci QrCode qr; 1055317bbafSopenharmony_ci List<QrSegment> segs; 1065317bbafSopenharmony_ci 1075317bbafSopenharmony_ci // Illustration "silver" 1085317bbafSopenharmony_ci String silver0 = "THE SQUARE ROOT OF 2 IS 1."; 1095317bbafSopenharmony_ci String silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; 1105317bbafSopenharmony_ci qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); 1115317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "sqrt2-monolithic-QR.png"); 1125317bbafSopenharmony_ci 1135317bbafSopenharmony_ci segs = Arrays.asList( 1145317bbafSopenharmony_ci QrSegment.makeAlphanumeric(silver0), 1155317bbafSopenharmony_ci QrSegment.makeNumeric(silver1)); 1165317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); 1175317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "sqrt2-segmented-QR.png"); 1185317bbafSopenharmony_ci 1195317bbafSopenharmony_ci // Illustration "golden" 1205317bbafSopenharmony_ci String golden0 = "Golden ratio φ = 1."; 1215317bbafSopenharmony_ci String golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; 1225317bbafSopenharmony_ci String golden2 = "......"; 1235317bbafSopenharmony_ci qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); 1245317bbafSopenharmony_ci writePng(toImage(qr, 8, 5), "phi-monolithic-QR.png"); 1255317bbafSopenharmony_ci 1265317bbafSopenharmony_ci segs = Arrays.asList( 1275317bbafSopenharmony_ci QrSegment.makeBytes(golden0.getBytes(StandardCharsets.UTF_8)), 1285317bbafSopenharmony_ci QrSegment.makeNumeric(golden1), 1295317bbafSopenharmony_ci QrSegment.makeAlphanumeric(golden2)); 1305317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); 1315317bbafSopenharmony_ci writePng(toImage(qr, 8, 5), "phi-segmented-QR.png"); 1325317bbafSopenharmony_ci 1335317bbafSopenharmony_ci // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters 1345317bbafSopenharmony_ci String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; 1355317bbafSopenharmony_ci qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); 1365317bbafSopenharmony_ci writePng(toImage(qr, 9, 4, 0xFFFFE0, 0x303080), "madoka-utf8-QR.png"); 1375317bbafSopenharmony_ci 1385317bbafSopenharmony_ci segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka)); 1395317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); 1405317bbafSopenharmony_ci writePng(toImage(qr, 9, 4, 0xE0F0FF, 0x404040), "madoka-kanji-QR.png"); 1415317bbafSopenharmony_ci } 1425317bbafSopenharmony_ci 1435317bbafSopenharmony_ci 1445317bbafSopenharmony_ci // Creates QR Codes with the same size and contents but different mask patterns. 1455317bbafSopenharmony_ci private static void doMaskDemo() throws IOException { 1465317bbafSopenharmony_ci QrCode qr; 1475317bbafSopenharmony_ci List<QrSegment> segs; 1485317bbafSopenharmony_ci 1495317bbafSopenharmony_ci // Project Nayuki URL 1505317bbafSopenharmony_ci segs = QrSegment.makeSegments("https://www.nayuki.io/"); 1515317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask 1525317bbafSopenharmony_ci writePng(toImage(qr, 8, 6, 0xE0FFE0, 0x206020), "project-nayuki-automask-QR.png"); 1535317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 1545317bbafSopenharmony_ci writePng(toImage(qr, 8, 6, 0xFFE0E0, 0x602020), "project-nayuki-mask3-QR.png"); 1555317bbafSopenharmony_ci 1565317bbafSopenharmony_ci // Chinese text as UTF-8 1575317bbafSopenharmony_ci segs = QrSegment.makeSegments("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"); 1585317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 1595317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "unicode-mask0-QR.png"); 1605317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 1615317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "unicode-mask1-QR.png"); 1625317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 1635317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "unicode-mask5-QR.png"); 1645317bbafSopenharmony_ci qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 1655317bbafSopenharmony_ci writePng(toImage(qr, 10, 3), "unicode-mask7-QR.png"); 1665317bbafSopenharmony_ci } 1675317bbafSopenharmony_ci 1685317bbafSopenharmony_ci 1695317bbafSopenharmony_ci 1705317bbafSopenharmony_ci /*---- Utilities ----*/ 1715317bbafSopenharmony_ci 1725317bbafSopenharmony_ci private static BufferedImage toImage(QrCode qr, int scale, int border) { 1735317bbafSopenharmony_ci return toImage(qr, scale, border, 0xFFFFFF, 0x000000); 1745317bbafSopenharmony_ci } 1755317bbafSopenharmony_ci 1765317bbafSopenharmony_ci 1775317bbafSopenharmony_ci /** 1785317bbafSopenharmony_ci * Returns a raster image depicting the specified QR Code, with 1795317bbafSopenharmony_ci * the specified module scale, border modules, and module colors. 1805317bbafSopenharmony_ci * <p>For example, scale=10 and border=4 means to pad the QR Code with 4 light border 1815317bbafSopenharmony_ci * modules on all four sides, and use 10×10 pixels to represent each module. 1825317bbafSopenharmony_ci * @param qr the QR Code to render (not {@code null}) 1835317bbafSopenharmony_ci * @param scale the side length (measured in pixels, must be positive) of each module 1845317bbafSopenharmony_ci * @param border the number of border modules to add, which must be non-negative 1855317bbafSopenharmony_ci * @param lightColor the color to use for light modules, in 0xRRGGBB format 1865317bbafSopenharmony_ci * @param darkColor the color to use for dark modules, in 0xRRGGBB format 1875317bbafSopenharmony_ci * @return a new image representing the QR Code, with padding and scaling 1885317bbafSopenharmony_ci * @throws NullPointerException if the QR Code is {@code null} 1895317bbafSopenharmony_ci * @throws IllegalArgumentException if the scale or border is out of range, or if 1905317bbafSopenharmony_ci * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE 1915317bbafSopenharmony_ci */ 1925317bbafSopenharmony_ci private static BufferedImage toImage(QrCode qr, int scale, int border, int lightColor, int darkColor) { 1935317bbafSopenharmony_ci Objects.requireNonNull(qr); 1945317bbafSopenharmony_ci if (scale <= 0 || border < 0) 1955317bbafSopenharmony_ci throw new IllegalArgumentException("Value out of range"); 1965317bbafSopenharmony_ci if (border > Integer.MAX_VALUE / 2 || qr.size + border * 2L > Integer.MAX_VALUE / scale) 1975317bbafSopenharmony_ci throw new IllegalArgumentException("Scale or border too large"); 1985317bbafSopenharmony_ci 1995317bbafSopenharmony_ci BufferedImage result = new BufferedImage((qr.size + border * 2) * scale, (qr.size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); 2005317bbafSopenharmony_ci for (int y = 0; y < result.getHeight(); y++) { 2015317bbafSopenharmony_ci for (int x = 0; x < result.getWidth(); x++) { 2025317bbafSopenharmony_ci boolean color = qr.getModule(x / scale - border, y / scale - border); 2035317bbafSopenharmony_ci result.setRGB(x, y, color ? darkColor : lightColor); 2045317bbafSopenharmony_ci } 2055317bbafSopenharmony_ci } 2065317bbafSopenharmony_ci return result; 2075317bbafSopenharmony_ci } 2085317bbafSopenharmony_ci 2095317bbafSopenharmony_ci 2105317bbafSopenharmony_ci // Helper function to reduce code duplication. 2115317bbafSopenharmony_ci private static void writePng(BufferedImage img, String filepath) throws IOException { 2125317bbafSopenharmony_ci ImageIO.write(img, "png", new File(filepath)); 2135317bbafSopenharmony_ci } 2145317bbafSopenharmony_ci 2155317bbafSopenharmony_ci 2165317bbafSopenharmony_ci /** 2175317bbafSopenharmony_ci * Returns a string of SVG code for an image depicting the specified QR Code, with the specified 2185317bbafSopenharmony_ci * number of border modules. The string always uses Unix newlines (\n), regardless of the platform. 2195317bbafSopenharmony_ci * @param qr the QR Code to render (not {@code null}) 2205317bbafSopenharmony_ci * @param border the number of border modules to add, which must be non-negative 2215317bbafSopenharmony_ci * @param lightColor the color to use for light modules, in any format supported by CSS, not {@code null} 2225317bbafSopenharmony_ci * @param darkColor the color to use for dark modules, in any format supported by CSS, not {@code null} 2235317bbafSopenharmony_ci * @return a string representing the QR Code as an SVG XML document 2245317bbafSopenharmony_ci * @throws NullPointerException if any object is {@code null} 2255317bbafSopenharmony_ci * @throws IllegalArgumentException if the border is negative 2265317bbafSopenharmony_ci */ 2275317bbafSopenharmony_ci private static String toSvgString(QrCode qr, int border, String lightColor, String darkColor) { 2285317bbafSopenharmony_ci Objects.requireNonNull(qr); 2295317bbafSopenharmony_ci Objects.requireNonNull(lightColor); 2305317bbafSopenharmony_ci Objects.requireNonNull(darkColor); 2315317bbafSopenharmony_ci if (border < 0) 2325317bbafSopenharmony_ci throw new IllegalArgumentException("Border must be non-negative"); 2335317bbafSopenharmony_ci long brd = border; 2345317bbafSopenharmony_ci StringBuilder sb = new StringBuilder() 2355317bbafSopenharmony_ci .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") 2365317bbafSopenharmony_ci .append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n") 2375317bbafSopenharmony_ci .append(String.format("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 %1$d %1$d\" stroke=\"none\">\n", 2385317bbafSopenharmony_ci qr.size + brd * 2)) 2395317bbafSopenharmony_ci .append("\t<rect width=\"100%\" height=\"100%\" fill=\"" + lightColor + "\"/>\n") 2405317bbafSopenharmony_ci .append("\t<path d=\""); 2415317bbafSopenharmony_ci for (int y = 0; y < qr.size; y++) { 2425317bbafSopenharmony_ci for (int x = 0; x < qr.size; x++) { 2435317bbafSopenharmony_ci if (qr.getModule(x, y)) { 2445317bbafSopenharmony_ci if (x != 0 || y != 0) 2455317bbafSopenharmony_ci sb.append(" "); 2465317bbafSopenharmony_ci sb.append(String.format("M%d,%dh1v1h-1z", x + brd, y + brd)); 2475317bbafSopenharmony_ci } 2485317bbafSopenharmony_ci } 2495317bbafSopenharmony_ci } 2505317bbafSopenharmony_ci return sb 2515317bbafSopenharmony_ci .append("\" fill=\"" + darkColor + "\"/>\n") 2525317bbafSopenharmony_ci .append("</svg>\n") 2535317bbafSopenharmony_ci .toString(); 2545317bbafSopenharmony_ci } 2555317bbafSopenharmony_ci 2565317bbafSopenharmony_ci} 257