├── .gitignore
├── screenshots
├── v0
│ └── qrcode_gen.PNG
├── v1
│ ├── qrcode_reader.png
│ └── qrcode_generator.png
└── v0.1
│ ├── qrcode_reader.png
│ └── qrcode_generator.png
├── src
└── main
│ ├── java
│ └── com
│ │ └── houarizegai
│ │ └── qrcodefx
│ │ ├── global
│ │ ├── FXTools.java
│ │ └── Utils.java
│ │ ├── App.java
│ │ ├── engine
│ │ └── QRCodeEngine.java
│ │ └── controllers
│ │ └── QRCodeController.java
│ └── resources
│ ├── styles
│ └── qrcode.css
│ └── fxml
│ └── QRCode.fxml
├── README.md
├── LICENSE
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Intellij
2 | .idea/
3 | out/
4 | *.iml
5 |
6 | # Maven
7 | log/
8 | target/
9 |
--------------------------------------------------------------------------------
/screenshots/v0/qrcode_gen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouariZegai/QRCodeFX/HEAD/screenshots/v0/qrcode_gen.PNG
--------------------------------------------------------------------------------
/screenshots/v1/qrcode_reader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouariZegai/QRCodeFX/HEAD/screenshots/v1/qrcode_reader.png
--------------------------------------------------------------------------------
/screenshots/v0.1/qrcode_reader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouariZegai/QRCodeFX/HEAD/screenshots/v0.1/qrcode_reader.png
--------------------------------------------------------------------------------
/screenshots/v1/qrcode_generator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouariZegai/QRCodeFX/HEAD/screenshots/v1/qrcode_generator.png
--------------------------------------------------------------------------------
/screenshots/v0.1/qrcode_generator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HouariZegai/QRCodeFX/HEAD/screenshots/v0.1/qrcode_generator.png
--------------------------------------------------------------------------------
/src/main/java/com/houarizegai/qrcodefx/global/FXTools.java:
--------------------------------------------------------------------------------
1 | package com.houarizegai.qrcodefx.global;
2 |
3 | import com.jfoenix.controls.JFXTextField;
4 | import javafx.scene.paint.Color;
5 |
6 | public class FXTools {
7 | public static void fieldAcceptNumbersOnly(JFXTextField field) {
8 | field.textProperty().addListener((observable, oldValue, newValue) -> {
9 | if (!newValue.matches("\\d*"))
10 | field.setText(newValue.replaceAll("[^\\d]", ""));
11 | });
12 | }
13 |
14 | public static String toRGBCode(Color color ) {
15 | return String.format( "#%02X%02X%02X",
16 | (int)( color.getRed() * 255 ),
17 | (int)( color.getGreen() * 255 ),
18 | (int)( color.getBlue() * 255 ) );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/houarizegai/qrcodefx/App.java:
--------------------------------------------------------------------------------
1 | package com.houarizegai.qrcodefx;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Parent;
6 | import javafx.scene.Scene;
7 | import javafx.stage.Stage;
8 |
9 | import java.io.IOException;
10 |
11 | public class App extends Application {
12 | public static Stage stage;
13 |
14 | @Override
15 | public void start(Stage stage) {
16 | try {
17 | Parent root = FXMLLoader.load(getClass().getResource("/fxml/QRCode.fxml"));
18 |
19 | stage.setScene(new Scene(root));
20 | stage.setTitle("QR Code");
21 |
22 | App.stage = stage;
23 | stage.show();
24 | } catch(IOException ioe) {
25 | ioe.printStackTrace();
26 | }
27 | }
28 |
29 | public static void main(String[] args) {
30 | launch(args);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## QR Code FX
2 | 
3 | [](LICENSE)
4 |
5 | #### Features
6 | * Very simple to use
7 | * Generate QR Code image from text and export the generated image (Encoder)
8 | * Read text from QR Code image and export the generated text (Decoder)
9 |
10 | #### Requirements
11 | * Java 8
12 |
13 | #### Screenshoot
14 | ##### Dark:
15 | | QR Code Generator | QR Code Reader |
16 | |:-------------------:|:-------------------:|
17 | |  |  |
18 |
19 | ##### Light:
20 | | QR Code Generator | QR Code Reader |
21 | |:-------------------:|:-------------------:|
22 | |  |  |
23 |
24 | #### Contributing 💡
25 | If you want to contribute to this project and make it better with new ideas, your pull request is very welcomed.
26 | If you find any issue just put it in the repository issue section, thank you.
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Houari ZEGAI
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/java/com/houarizegai/qrcodefx/global/Utils.java:
--------------------------------------------------------------------------------
1 | package com.houarizegai.qrcodefx.global;
2 |
3 | import javafx.embed.swing.SwingFXUtils;
4 | import javafx.scene.image.Image;
5 |
6 | import javax.imageio.ImageIO;
7 | import java.awt.image.BufferedImage;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.io.PrintWriter;
11 |
12 | public class Utils {
13 |
14 | public static boolean exportImage(Image image, String formatName, File outputFile) {
15 | BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
16 | try {
17 | ImageIO.write(bImage, formatName, outputFile);
18 | return true;
19 | } catch (IOException e) {
20 | System.out.println("Cannot export image!");
21 | e.printStackTrace();
22 | return false;
23 | }
24 | }
25 |
26 | public static boolean exportText(String text, File outputFile) {
27 | try {
28 | PrintWriter out = new PrintWriter(outputFile);
29 | out.print(text);
30 | out.close();
31 |
32 | return true;
33 | } catch(IOException ioe) {
34 | System.out.println("Cannot export text!");
35 | ioe.printStackTrace();
36 | return false;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.houarizegai
8 | qrcodegenfx
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 1.8
13 | 1.8
14 | 2.0
15 |
16 |
17 |
18 |
19 |
20 | com.jfoenix
21 | jfoenix
22 | 8.0.1
23 |
24 |
25 |
26 | com.google.zxing
27 | core
28 | ${com.google.zxing}
29 |
30 |
31 |
32 | com.google.zxing
33 | javase
34 | ${com.google.zxing}
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/main/java/com/houarizegai/qrcodefx/engine/QRCodeEngine.java:
--------------------------------------------------------------------------------
1 | package com.houarizegai.qrcodefx.engine;
2 |
3 | import com.google.zxing.*;
4 | import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
5 | import com.google.zxing.common.BitMatrix;
6 | import com.google.zxing.common.HybridBinarizer;
7 | import com.google.zxing.qrcode.QRCodeWriter;
8 | import javafx.embed.swing.SwingFXUtils;
9 | import javafx.scene.image.Image;
10 |
11 | import java.awt.*;
12 | import java.awt.image.BufferedImage;
13 |
14 | public class QRCodeEngine {
15 |
16 | public static Image encode(String data, int width, int height, String foregroundColor, String backgroundColor) {
17 | try {
18 | BitMatrix byteMatrix = new QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, width, height);
19 | BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
20 | bufferedImage.createGraphics();
21 | Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
22 | graphics.setColor(Color.decode(backgroundColor));
23 | graphics.fillRect(0, 0, width, height);
24 | graphics.setColor(Color.decode(foregroundColor));
25 | for (int i = 0; i < height; i++) {
26 | for (int j = 0; j < width; j++) {
27 | if (byteMatrix.get(i, j)) {
28 | graphics.fillRect(i, j, 1, 1);
29 | }
30 | }
31 | }
32 |
33 | return SwingFXUtils.toFXImage(bufferedImage, null);
34 | } catch (WriterException ex) {
35 | ex.printStackTrace();
36 | return null;
37 | }
38 | }
39 |
40 | public static String decode(Image image) {
41 | BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
42 | new BufferedImageLuminanceSource(SwingFXUtils.fromFXImage(image, null))));
43 |
44 | try {
45 | Result qrCodeResult = new MultiFormatReader().decode(binaryBitmap);
46 | return qrCodeResult.getText();
47 | } catch(NotFoundException nfe) {
48 | System.out.println("Not found Exception!");
49 | return null;
50 | }
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/resources/styles/qrcode.css:
--------------------------------------------------------------------------------
1 | * {
2 | -fx-font-family: Tahoma;
3 | -color-primary: #222831;
4 | -color-secondary: #00adb5;
5 | -color-primary-light: #393e46;
6 | }
7 |
8 | .root {
9 | -fx-background-color: -color-primary
10 | }
11 |
12 | .title {
13 | -fx-font-size: 20px;
14 | -fx-text-fill: -color-secondary
15 | }
16 |
17 | /* Start jfxtabpane */
18 |
19 | .tab-title {
20 | -fx-padding: 5px 0;
21 | }
22 |
23 | .jfx-tab-pane .headers-region {
24 | -fx-background-color: transparent;
25 | }
26 |
27 | .jfx-tab-pane .tab-header-background {
28 | -fx-background-color: -color-primary;
29 | }
30 |
31 | .jfx-tab-pane .tab-selected-line {
32 | -fx-background-color: -color-secondary;
33 | }
34 |
35 | .jfx-tab-pane .tab {
36 | -fx-cursor: hand;
37 | }
38 |
39 | .jfx-tab-pane .tab .tab-label {
40 | -fx-alignment: CENTER;
41 | -fx-font-family: Tahoma;
42 | -fx-text-fill: -color-secondary;
43 | -fx-font-size: 16px;
44 | -fx-font-weight: normal;
45 | }
46 |
47 | .tab:selected .tab-label {
48 | -fx-alignment: CENTER;
49 | -fx-font-family: Tahoma;
50 | -fx-text-fill: -color-secondary;
51 | -fx-font-size: 16px;
52 | -fx-font-weight: normal;
53 | }
54 |
55 | /* End jfxtabpane */
56 |
57 | .box-header {
58 | -fx-background-color: -color-primary-light;
59 | -fx-effect: dropshadow(three-pass-box, rgba(100,100,100,1), 2, 0, 0, 0);
60 | -fx-background-radius: 2;
61 | }
62 |
63 | .box-title {
64 | -fx-font-size: 16px;
65 | -fx-text-fill: -color-secondary
66 | }
67 |
68 | .box-area {
69 | -fx-font-size: 15px;
70 | -fx-text-fill: #DDD;
71 | }
72 |
73 | .box-area .content {
74 | -fx-background-color: -color-primary;
75 | }
76 |
77 | .btn {
78 | -fx-pref-width: 100px;
79 | -fx-pref-height: 35px;
80 | -fx-font-size: 16px;
81 | -fx-background-color: #999;
82 | -fx-text-fill: #FFF;
83 | -jfx-button-type: RAISED;
84 | -fx-cursor: hand
85 | }
86 |
87 | .btn-success {-fx-background-color: #4285f4;}
88 |
89 | .btn-primary {-fx-background-color: #FF8800;}
90 |
91 | .field-size {
92 | -fx-pref-width: 80px;
93 | -fx-pref-height: 35px;
94 | -fx-font-size: 15px;
95 | -jfx-label-float: true;
96 | -fx-prompt-text-fill: #AAA;
97 | -jfx-unfocus-color: #AAA;
98 | -jfx-focus-color: -color-secondary;
99 | }
100 |
101 | .lbl {
102 | -fx-text-fill: #AAA;
103 | -fx-font-size: 15px;
104 | }
105 |
106 | /* Start JFXSnackbar (toast) */
107 |
108 | .jfx-snackbar-content {
109 | -fx-background-color: #F00;
110 | -fx-padding: 0 20px 0 20px
111 | }
112 | .jfx-snackbar-toast {
113 | -fx-text-fill: #FFF;
114 | -fx-font-size: 18px;
115 | -fx-font-family: Tahoma
116 | }
117 |
118 | /* End JFXSnackbar (toast) */
119 |
120 | .footer-text {
121 | -fx-font-size: 14px;
122 | -fx-text-fill: #777
123 | }
--------------------------------------------------------------------------------
/src/main/java/com/houarizegai/qrcodefx/controllers/QRCodeController.java:
--------------------------------------------------------------------------------
1 | package com.houarizegai.qrcodefx.controllers;
2 |
3 | import com.houarizegai.qrcodefx.App;
4 | import com.houarizegai.qrcodefx.engine.QRCodeEngine;
5 | import com.houarizegai.qrcodefx.global.FXTools;
6 | import com.houarizegai.qrcodefx.global.Utils;
7 | import com.jfoenix.controls.JFXColorPicker;
8 | import com.jfoenix.controls.JFXSnackbar;
9 | import com.jfoenix.controls.JFXTextField;
10 | import javafx.fxml.FXML;
11 | import javafx.fxml.Initializable;
12 | import javafx.scene.control.TextArea;
13 | import javafx.scene.image.Image;
14 | import javafx.scene.image.ImageView;
15 | import javafx.scene.layout.VBox;
16 | import javafx.stage.FileChooser;
17 |
18 | import java.io.File;
19 | import java.net.URL;
20 | import java.util.ResourceBundle;
21 |
22 | public class QRCodeController implements Initializable {
23 |
24 | @FXML
25 | private VBox root;
26 |
27 | /* Start generator attributes */
28 | @FXML
29 | private TextArea areaInputGen;
30 |
31 | @FXML
32 | private JFXTextField fieldWidthGen, fieldHeightGen;
33 |
34 | @FXML
35 | private JFXColorPicker colorPickerForeground, colorPickerBackground;
36 |
37 | @FXML
38 | private ImageView imgQRCodeGen;
39 |
40 | private Image genQRCodeImg; // Generated QR Code image
41 |
42 | /* End generator attributes */
43 |
44 | /* Start reader attributes */
45 |
46 | @FXML
47 | private ImageView imgQRCodeReader;
48 |
49 | @FXML
50 | private TextArea areaResultReader;
51 |
52 | /* End reader attributes */
53 |
54 | private FileChooser imageChooser;
55 | private FileChooser pngImageSaveChooser;
56 | private FileChooser textSaveChooser;
57 |
58 | private JFXSnackbar toastError;
59 |
60 | @Override
61 | public void initialize(URL location, ResourceBundle resources) {
62 | // Make fields accept numbers only
63 | FXTools.fieldAcceptNumbersOnly(fieldWidthGen);
64 | FXTools.fieldAcceptNumbersOnly(fieldHeightGen);
65 |
66 | // Init toast error message
67 | toastError = new JFXSnackbar(root);
68 |
69 | // Init fields
70 | fieldWidthGen.setText("200");
71 | fieldHeightGen.setText("200");
72 |
73 | // Init file chooser
74 | pngImageSaveChooser = new FileChooser();
75 | pngImageSaveChooser.setTitle("Save Image");
76 | pngImageSaveChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Image (*.png)", "*.png "));
77 |
78 | // Init File chooser for image
79 | imageChooser = new FileChooser();
80 | imageChooser.setTitle("Select Image File");
81 | imageChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Image File", "*.png", "*.jpg", "*.jpeg"));
82 |
83 | // Init File chooser for saving text
84 | textSaveChooser = new FileChooser();
85 | textSaveChooser.setTitle("Select Saving File");
86 | textSaveChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Text File (*.txt)", "*.txt"));
87 | }
88 |
89 | /* Start generator methods */
90 |
91 | @FXML
92 | public void onGenerate() {
93 | String foregroundColor = FXTools.toRGBCode(colorPickerForeground.getValue());
94 | String backgroundColor = FXTools.toRGBCode(colorPickerBackground.getValue());
95 |
96 | genQRCodeImg = QRCodeEngine.encode(areaInputGen.getText(), Integer.parseInt(fieldWidthGen.getText()), Integer.parseInt(fieldHeightGen.getText()), foregroundColor, backgroundColor);
97 | if(genQRCodeImg != null) {
98 | imgQRCodeGen.setImage(genQRCodeImg);
99 | }
100 | }
101 |
102 | @FXML
103 | public void onExportGen() {
104 | if(genQRCodeImg == null)
105 | return;
106 |
107 | // Choose path of save
108 | File file = pngImageSaveChooser.showSaveDialog(App.stage);
109 | // Write in a file
110 | if (file != null) {
111 | Utils.exportImage(genQRCodeImg, "png", file);
112 | }
113 | }
114 |
115 | /* End generator methods */
116 |
117 | /* Start reader methods */
118 |
119 | @FXML
120 | public void onLoadReader() {
121 | File file = imageChooser.showOpenDialog(App.stage);
122 |
123 | if(file != null) {
124 | areaResultReader.setText(null);
125 | imgQRCodeReader.setImage(new Image(file.toURI().toString()));
126 | }
127 | }
128 |
129 | @FXML
130 | public void onRead() {
131 | if(imgQRCodeReader.getImage() == null) {
132 | toastError.show("Please, load an image to read it!", 1500);
133 | return;
134 | }
135 |
136 | String decodedText = QRCodeEngine.decode(imgQRCodeReader.getImage());
137 | if(decodedText == null) {
138 | toastError.show("Error decoding QR Code image!", 1500);
139 | } else
140 | areaResultReader.setText(decodedText);
141 | }
142 |
143 | @FXML
144 | public void onExportReader() {
145 | if(areaResultReader.getText() == null && areaResultReader.getText().trim().isEmpty())
146 | return;
147 |
148 | // Choose path of save
149 | File file = textSaveChooser.showSaveDialog(App.stage);
150 | // Write in a file
151 | if (file != null) {
152 | Utils.exportText(areaResultReader.getText().trim(), file);
153 | }
154 | }
155 |
156 | /* End reader methods */
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/resources/fxml/QRCode.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------