Skip to content

Commit 47f17b3

Browse files
committed
add template processing
1 parent 1d23b7a commit 47f17b3

File tree

11 files changed

+261
-32
lines changed

11 files changed

+261
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target/

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ This tool converts images files into a string of hexadecimal bytes which can be
88
- Bitmap (BMP)
99
- Graphics Interchange Format (GIF)
1010
- Portable Network Graphics (PNG)
11-
- JPEG File Interchange Format (JPG / JPEG) - not recommended, based on the compression rate and the resulting artefacts the result might not be what you expect
11+
- JPEG File Interchange Format (JPG / JPEG) - not recommended, based on the compression rate and hence artefacts the result might not look as you expect
1212

13-
A pixel with white color will be converted as "LED = on" any other pixel will be converted as "LED = off". Best results you will get with monochrom images.
13+
A pixel with white color is converted as "LED = on" any other pixel is converted as "LED = off". Best results you get with monochrom images.
1414

1515
**supported image size**
1616

@@ -35,4 +35,33 @@ printf '\eHL\\00 70 01 1F 08 00 20 42 40 5C 40 42 20\\ \\00 1F FF\\' > /dev/ttyU
3535

3636
For further information about the above command have a look on the Hacklace 2 wiki[1].
3737

38+
**usage - convert a template file**
39+
40+
A template file is a Hacklace command script or a Shell script inlcuding a output command (see above example) which includes a placeholders for an image file. If the image file from the placeholderncan be found the placeholder is substituted by hexadecimal bytes representing the image file.
41+
42+
template_example.txt
43+
```
44+
HL
45+
\00 7A 01\ It's hip to be [[square.png]] ! \00\
46+
\00 7A 01\ It's hip to [[smile.png]] ! \00\
47+
\FF\
48+
```
49+
50+
```
51+
java -jar ImageToHacklace2.jar --template template_example.txt
52+
```
53+
54+
Assuming the image file `square.png` exist in the current directory and the image file `smile.png` does not exist. The file `template_example.txt` will be converted to `template_example.hl` like below.
55+
56+
template_example.hl
57+
```
58+
HL
59+
\00 7A 01\ It's hip to be \1F 08 00 7E 42 42 42 42 7E 00\ ! \00\
60+
\00 7A 01\ It's hip to [[smile.png]] ! \00\
61+
\FF\
62+
```
63+
64+
For further information about the format of a hacklace script have a look on the Hacklace 2 wiki[1].
65+
66+
3867
[1] http://wiki.fab4u.de/wiki/Hacklace

TODO.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
1-
**for next version**
2-
- add process fo hacklace template files
3-
- add documentation
4-
5-
**for the future**
1+
**planned for next releases**
62
- add a basic image editor to generate image files

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
**version 0.1.1**
2+
- add processing of Hacklace template files
3+
- documentation updated
4+
5+
**version 0.1.0**
6+
- initial version

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>sub.optimal</groupId>
66
<artifactId>ImageToHacklace2</artifactId>
7-
<version>0.1.0</version>
7+
<version>0.1.1</version>
88
<packaging>jar</packaging>
99

1010
<name>ImageToHacklace2</name>

src/main/java/sub/optimal/Img2Hl.java

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,52 +18,68 @@
1818
package sub.optimal;
1919

2020
import java.io.File;
21+
import java.io.IOException;
22+
import java.util.logging.Level;
23+
import java.util.logging.Logger;
2124
import sub.optimal.hacklace2.Converter;
25+
import sub.optimal.hacklace2.TemplateProcessor;
26+
import sub.optimal.util.FileUtil;
2227

2328
/**
2429
* Main class of ImageToHacklace2 converter.<br><br>
2530
* This tool converts images files into a string of hexadecimal bytes which can be downloaded<br>
26-
* as part of an animation onto the "HackLace2"
27-
* (see <a href="http://wiki.fab4u.de/wiki/Hacklace">http://wiki.fab4u.de/wiki/Hacklace</a>).
31+
* as part of an animation onto the "HackLace2" (see <a
32+
* href="http://wiki.fab4u.de/wiki/Hacklace">http://wiki.fab4u.de/wiki/Hacklace</a>).
2833
*
2934
* @author SubOptimal
3035
* @version 0.1.0
3136
*/
3237
public class Img2Hl {
33-
private static final String VERSION = "0.1.0";
38+
39+
private static final String VERSION = "0.1.1";
3440

3541
private static String templateFileName;
3642
private static String imageFileName;
3743

3844
public static void main(String[] args) {
3945
processParameters(args);
4046
if (imageFileName != null) {
41-
processImageFile();
47+
processImageFile();
4248
} else if (templateFileName != null) {
4349
processTemplateFile();
4450
}
4551
}
4652

53+
/**
54+
* Process the template file.
55+
*/
4756
private static void processTemplateFile() {
48-
throw new UnsupportedOperationException("Not supported yet - see TODO");
57+
File template = new File(templateFileName);
58+
if (FileUtil.isReadableFile(template)) {
59+
TemplateProcessor converter = TemplateProcessor.getInstance();
60+
try {
61+
converter.processTemplateFile(template);
62+
} catch (IOException ex) {
63+
System.err.println("convertion failed: " + ex.getMessage());
64+
}
65+
}
4966
}
5067

68+
/**
69+
* Process an single image file.
70+
*/
5171
private static void processImageFile() {
5272
File image = new File(imageFileName);
53-
if (!image.exists() || !image.isFile()) {
54-
System.err.println(String.format("file %s: does not exist", image.getName()));
55-
System.exit(1);
73+
if (FileUtil.isReadableFile(image)) {
74+
Converter converter = Converter.getInstance();
75+
System.out.println(converter.convertToHacklace(image));
5676
}
57-
58-
if (!image.canRead()) {
59-
System.err.println(String.format("file %s: no read permission", image.getName()));
60-
System.exit(1);
61-
}
62-
63-
Converter converter = Converter.getInstance();
64-
System.out.println(converter.convertToHacklace(image));
6577
}
6678

79+
/**
80+
* Process the passed application parameters.
81+
* @param args
82+
*/
6783
private static void processParameters(String[] args) {
6884
if (args.length == 0) {
6985
showUsage();
@@ -73,13 +89,12 @@ private static void processParameters(String[] args) {
7389
for (int i = 0; i < args.length; i++) {
7490
String opt = args[i];
7591
if (opt.startsWith("--template")) {
76-
// TODO will be implemented as next ;-)
77-
processTemplateFile();
7892
if ((i + 1) < args.length) {
7993
templateFileName = args[++i];
8094
} else {
81-
System.err.println("missed parameter for option --template");
95+
System.err.println("parameter missed for option --template");
8296
System.exit(1);
97+
processTemplateFile();
8398
}
8499
} else if (opt.startsWith("--")) {
85100
System.err.printf("unknown option passed '%s'%n", opt);

src/main/java/sub/optimal/hacklace2/Converter.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,41 @@
2828
*/
2929
public class Converter {
3030

31-
public static final String HEX_BYTE_MARKER = "\\";
31+
private static final String HEX_BYTE_MARKER = "\\";
3232
public static final String DATABLOCK_START = HEX_BYTE_MARKER + "1F";
3333
public static final int RGB_WHITE = 16777215;
3434
private static final String ZERO_LENGTH_STRING = "";
3535

36+
private Converter() {
37+
}
38+
39+
/**
40+
* Return an instance of the converter.
41+
*
42+
* @return new instance of the {@link Converter}
43+
*/
3644
public static Converter getInstance() {
3745
return new Converter();
3846
}
3947

48+
/**
49+
* Return the marker which indicate the start/end of hexadecimal bytes in the Hacklace command.
50+
*
51+
* @return the hexadecimal bytes marker
52+
*/
53+
public static String getHexByteMarker() {
54+
return HEX_BYTE_MARKER;
55+
}
56+
57+
/**
58+
* Check if the passed image dimension are in the supported output range.
59+
*
60+
* @param height heigth of the image
61+
* @param width width of the image
62+
* @return false - if the dimension is below the minimal supported dimension<br>
63+
* true - if the dimension is in the supported range, in case the dimension is out the maximum
64+
* convertable range an warning is printed
65+
*/
4066
private boolean checkDimension(int height, int width) {
4167
if (height < 1 || width < 1) {
4268
System.err.println("image dimension out of range: minimal dimension = 1 x 1 pixel");
@@ -51,9 +77,20 @@ private boolean checkDimension(int height, int width) {
5177
return true;
5278
}
5379

80+
/**
81+
* Convert the passed image file into a string of hexadecimal bytes which can be passed to the
82+
* Hacklace 2 animation app.
83+
*
84+
* @param image image file
85+
* @return the hexadecimal bytes which represents the passed image
86+
*/
5487
public String convertToHacklace(File image) {
5588
try {
5689
BufferedImage buffImage = ImageIO.read(image);
90+
if (buffImage == null) {
91+
System.err.print(image.getName() + ": not supported image file format");
92+
return ZERO_LENGTH_STRING;
93+
}
5794
int height = buffImage.getHeight();
5895
int width = buffImage.getWidth();
5996
if (!checkDimension(height, width)) {
@@ -76,9 +113,8 @@ public String convertToHacklace(File image) {
76113
out.append(HEX_BYTE_MARKER);
77114
return out.toString();
78115
} catch (IOException ex) {
79-
System.err.println("failure during reading image file: " + ex.getMessage());
116+
System.err.println(image.getName() + ": failure during processing - " + ex.getMessage());
80117
return ZERO_LENGTH_STRING;
81118
}
82119
}
83-
84120
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright (C) 2014 suboptimal
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 2
7+
* of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*/
18+
package sub.optimal.hacklace2;
19+
20+
import java.io.BufferedReader;
21+
import java.io.BufferedWriter;
22+
import java.io.File;
23+
import java.io.FileInputStream;
24+
import java.io.FileOutputStream;
25+
import java.io.IOException;
26+
import java.io.InputStreamReader;
27+
import java.io.OutputStreamWriter;
28+
import java.util.regex.Matcher;
29+
import java.util.regex.Pattern;
30+
import sub.optimal.util.FileUtil;
31+
32+
/**
33+
*
34+
* @author suboptimal
35+
*/
36+
public class TemplateProcessor {
37+
38+
/**
39+
* Pattern to match an image placeholder in the format <code>[[filename]]</code>.
40+
*/
41+
private static final Pattern placeholder = Pattern.compile("\\[\\[([^\\]]*)\\]\\]");
42+
private Converter converter;
43+
44+
private TemplateProcessor() {
45+
}
46+
47+
/**
48+
* Return an instance of the converter.
49+
*
50+
* @return new instance of the {@link Converter}
51+
*/
52+
public static TemplateProcessor getInstance() {
53+
return new TemplateProcessor();
54+
}
55+
56+
/**
57+
* Process a Hacklace script template. Placeholder for existing image files will be substituted.
58+
*
59+
* @param template Hacklace template file
60+
* @throws IOException if reading from the template file or writing to the output file fails
61+
*/
62+
public void processTemplateFile(File template) throws IOException {
63+
converter = Converter.getInstance();
64+
65+
if (!FileUtil.isReadableFile(template)) {
66+
System.err.println(String.format("%s: cannot be accessed", template.getName()));
67+
return;
68+
}
69+
70+
String templateFileName = template.getName();
71+
int indexLastDot = templateFileName.lastIndexOf(".");
72+
String baseName = templateFileName.substring(0, indexLastDot < 0 ? templateFileName.length() : indexLastDot);
73+
File hacklaceFile = new File(baseName + ".hl");
74+
if (FileUtil.isReadableFile(hacklaceFile)) {
75+
System.err.println(String.format("%s: output file already exist", hacklaceFile.getName()));
76+
return;
77+
}
78+
79+
BufferedReader reader = null;
80+
BufferedWriter writer = null;
81+
try {
82+
reader = new BufferedReader(new InputStreamReader(new FileInputStream(template), "ISO-8859-1"));
83+
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(hacklaceFile), "ISO-8859-1"));
84+
85+
String s;
86+
while ((s = reader.readLine()) != null) {
87+
writer.write(replacePlaceholder(s));
88+
// TODO check for DOS/UNIX lineends
89+
writer.write('\n');
90+
}
91+
} finally {
92+
if (writer != null) {
93+
try {
94+
writer.close();
95+
} catch (IOException ioe) {
96+
System.err.println(String.format("%s: failed to close the file - %s", hacklaceFile.getName(), ioe.getMessage()));
97+
}
98+
}
99+
if (reader != null) {
100+
try {
101+
reader.close();
102+
} catch (IOException ioe) {
103+
System.err.println(String.format("%s: failed to close the file - %s", hacklaceFile.getName(), ioe.getMessage()));
104+
}
105+
}
106+
}
107+
}
108+
109+
/**
110+
* Replace an image placeholder in the passed line by hexadecimal bytes in Hacklace 2 animation
111+
* format.<br>
112+
* The placeholder has following format: [[filename]]<br>
113+
* - if <code>filename</code> point to a readable file, the placeholder is substituted
114+
*
115+
* @param line input string
116+
* @return output line, placeholder for existing image files are substituted
117+
*/
118+
public String replacePlaceholder(String line) {
119+
Matcher matcher = placeholder.matcher(line);
120+
StringBuffer lineBuffer = new StringBuffer();
121+
StringBuilder matchBuffer = new StringBuilder();
122+
while (matcher.find()) {
123+
matchBuffer.setLength(0);
124+
String fileName = matcher.group(1);
125+
File file = new File(fileName);
126+
if (FileUtil.isReadableFile(file)) {
127+
String hacklaceBytes = converter.convertToHacklace(file);
128+
if (hacklaceBytes.length() != 0) {
129+
System.out.println(String.format("%s: image converted", file.getName()));
130+
matchBuffer.append(Converter.getHexByteMarker());
131+
matchBuffer.append(hacklaceBytes);
132+
matchBuffer.append(Converter.getHexByteMarker());
133+
} else {
134+
System.out.println(String.format("%s: image could not be not converted", file.getName()));
135+
matchBuffer.append(matcher.group());
136+
}
137+
} else {
138+
System.out.println(String.format("%s: image skipped", file.getName()));
139+
matchBuffer.append(matcher.group());
140+
}
141+
matcher.appendReplacement(lineBuffer, matchBuffer.toString());
142+
}
143+
matcher.appendTail(lineBuffer);
144+
return lineBuffer.toString();
145+
}
146+
}

0 commit comments

Comments
 (0)