The HTML implementation is working, but subpar, and with lots of copy-pasted code
The LocalReader is a work in progress
#TEST = path to main test source to compile
#JAR_FLAGS += a list of things to pack, each usually prefixed with "-C bin/"
#SJAR_FLAGS += a list of things to pack, each usually prefixed with "-C src/", for *-sources.jar files
+#TEST_PARAMS = any parameter to pass to the test runnable when "test-run"
JAVAC = javac
JAVAC_FLAGS += -encoding UTF-8 -d ./bin/ -cp ./src/
all: build jar
-.PHONY: all clean mrproper mrpropre build run jrun jar resources install libs love
+.PHONY: all clean mrproper mrpropre build run jrun jar resources test-resources install libs love
bin:
@mkdir -p bin
$(JAVAC) $(JAVAC_FLAGS) "src/$$sup.java" ; \
done
-test:
+test: test-resources
@[ -e bin/$(MAIN).class ] || echo You need to build the sources
@[ -e bin/$(MAIN).class ]
@echo Compiling test program...
resources: libs
@echo Copying resources into bin/...
- @cd src && find . | grep -v '\.java$$' | while read -r ln; do \
+ @cd src && find . | grep -v '\.java$$' | grep -v '/test/' | while read -r ln; do \
+ if [ -f "$$ln" ]; then \
+ dir="`dirname "$$ln"`"; \
+ mkdir -p "../bin/$$dir" ; \
+ cp "$$ln" "../bin/$$ln" ; \
+ fi ; \
+ done
+
+test-resources: resources
+ @echo Copying test resources into bin/...
+ @cd src && find . | grep -v '\.java$$' | grep '/test/' | while read -r ln; do \
if [ -f "$$ln" ]; then \
dir="`dirname "$$ln"`"; \
mkdir -p "../bin/$$dir" ; \
@[ "$(TEST)" = "" -o -e "bin/$(TEST).class" ]
@echo Running tests for "$(NAME)"...
@[ "$(TEST)" != "" ] || echo No test sources defined.
- [ "$(TEST)" = "" ] || $(JAVA) $(JAVA_FLAGS) $(TEST)
+ [ "$(TEST)" = "" ] || ( clear ; $(JAVA) $(JAVA_FLAGS) $(TEST) $(TEST_PARAMS) )
install:
@[ -e $(NAME).jar ] || echo You need to build the jar
[ $valid = false ] && exit 2
+if [ "`whereis tput`" = "tput:" ]; then
+ ok='"[ ok ]"';
+ ko='"[ !! ]"';
+ cols=80;
+else
+ ok='"`tput bold`[`tput setf 2` OK `tput init``tput bold`]`tput init`"';
+ ko='"`tput bold`[`tput setf 4` !! `tput init``tput bold`]`tput init`"';
+ cols='"`tput cols`"';
+fi;
+
echo "MAIN = be/nikiroo/fanfix/Main" > Makefile
+echo "TEST = " >> Makefile
+echo "TEST_PARAMS = $cols $ok $ko" >> Makefile
echo "NAME = fanfix" >> Makefile
echo "PREFIX = $PREFIX" >> Makefile
echo "JAR_FLAGS += -C bin/ org -C bin/ be -C ./ VERSION" >> Makefile
} else {
try {
BasicSupport support = BasicSupport.getSupport(source);
+
if (support != null) {
Story story = support.process(source);
CHAPTER_EN, //
@Meta(what = "Chapter identification string", where = "", format = "", info = "used to identify a starting chapter in text mode")
CHAPTER_FR, //
+ @Meta(what = "Output type", where = "Local Reader", format = "One of the known output type", info = "The type of output for the Local Reader for non-images documents")
+ LOCAL_READER_NON_IMAGES_DOCUMENT_TYPE, //
+ @Meta(what = "Output type", where = "Local Reader", format = "One of the known output type", info = "The type of output for the Local Reader for images documents")
+ LOCAL_READER_IMAGES_DOCUMENT_TYPE, //
+
}
# (WHAT: Chapter identification string)
# used to identify a starting chapter in text mode
CHAPTER_FR = Chapitre
+# (WHAT: Output type, WHERE: Local Reader, FORMAT: One of the known output type)
+# The type of output for the Local Reader for non-images documents
+LOCAL_READER_NON_IMAGES_DOCUMENT_TYPE = HTML
+# (WHAT: Output type, WHERE: Local Reader, FORMAT: One of the known output type)
+# The type of output for the Local Reader for images documents
+LOCAL_READER_IMAGES_DOCUMENT_TYPE = CBZ
LATEX_LANG_UNKNOWN = Unknown language: %s
# (WHAT: 'by' prefix before author name)
# used to output the author, make sure it is covered by Config.BYS for input detection
-BY = By
+BY = by
/**
* Call {@link OutputType#valueOf(String.toUpperCase())} but return NULL
- * for NULL instead of raising exception.
+ * for NULL and empty instead of raising an exception.
*
* @param typeName
* the possible type name
* @return NULL or the type
*/
public static OutputType valueOfNullOkUC(String typeName) {
- if (typeName == null) {
+ if (typeName == null || typeName.isEmpty()) {
return null;
}
case LATEX:
return new LaTeX().setType(type, infoCover, infoCover);
case HTML:
- return new Html().setType(type, false, false);
+ return new Html().setType(type, infoCover, infoCover);
}
}
package be.nikiroo.fanfix.output;
-//TODO: implement it for LocalReader
-class Html extends Text {
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.bundles.StringId;
+import be.nikiroo.fanfix.data.Chapter;
+import be.nikiroo.fanfix.data.Paragraph;
+import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
+import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.utils.StringUtils;
+
+class Html extends BasicOutput {
+ protected FileWriter writer;
+ private boolean inDialogue = false;
+ private boolean inNormal = false;
+
+ @Override
+ public File process(Story story, File targetDir, String targetName)
+ throws IOException {
+ File target = new File(targetDir, targetName);
+ target.mkdir();
+
+ targetName = new File(targetName, "index").getPath();
+
+ String targetNameOrig = targetName;
+ targetName += getDefaultExtension();
+
+ target = new File(targetDir, targetName);
+
+ writer = new FileWriter(target);
+ try {
+ super.process(story, targetDir, targetNameOrig);
+ } finally {
+ writer.close();
+ writer = null;
+ }
+
+ return target;
+ }
+
+ @Override
+ public String getDefaultExtension() {
+ return ".html";
+ }
+
+ @Override
+ protected void writeStoryHeader(Story story) throws IOException {
+ String title = "";
+ if (story.getMeta() != null) {
+ title = story.getMeta().getTitle();
+ }
+
+ writer.write("<!DOCTYPE html>");
+ writer.write("\n<html>");
+ writer.write("\n<head>");
+ writer.write("\n <meta http-equiv='content-type' content='text/html; charset=utf-8'>");
+ writer.write("\n <meta name='viewport' content='width=device-width, initial-scale=1.0'>");
+ writer.write("\n <title>" + StringUtils.xmlEscape(title) + "</title>");
+ writer.write("\n</head>");
+ writer.write("\n<body>\n");
+
+ writer.write("<h1>" + StringUtils.xmlEscape(title) + "</h1>\n\n");
+ }
+
+ @Override
+ protected void writeStoryFooter(Story story) throws IOException {
+ writer.write("</body>\n");
+ }
+
+ @Override
+ protected void writeChapterHeader(Chapter chap) throws IOException {
+ String txt;
+ if (chap.getName() != null && !chap.getName().isEmpty()) {
+ txt = Instance.getTrans().getString(StringId.CHAPTER_NAMED,
+ chap.getNumber(), chap.getName());
+ } else {
+ txt = Instance.getTrans().getString(StringId.CHAPTER_UNNAMED,
+ chap.getNumber());
+ }
+
+ writer.write("<h1>" + StringUtils.xmlEscape(txt) + "</h1>\n\n");
+
+ inDialogue = false;
+ inNormal = false;
+ }
+
+ @Override
+ protected void writeParagraphHeader(Paragraph para) throws IOException {
+ if (para.getType() == ParagraphType.QUOTE && !inDialogue) {
+ writer.write(" <div class='dialogues'>\n");
+ inDialogue = true;
+ } else if (para.getType() != ParagraphType.QUOTE && inDialogue) {
+ writer.write(" </div>\n");
+ inDialogue = false;
+ }
+
+ if (para.getType() == ParagraphType.NORMAL && !inNormal) {
+ writer.write(" <div class='normals'>\n");
+ inNormal = true;
+ } else if (para.getType() != ParagraphType.NORMAL && inNormal) {
+ writer.write(" </div>\n");
+ inNormal = false;
+ }
+
+ switch (para.getType()) {
+ case BLANK:
+ writer.write(" <div class='blank'></div>");
+ break;
+ case BREAK:
+ writer.write(" <hr/>");
+ break;
+ case NORMAL:
+ writer.write(" <span class='normal'>");
+ break;
+ case QUOTE:
+ writer.write(" <div class='dialogue'>— ");
+ break;
+ case IMAGE:
+ // TODO
+ writer.write("<a href='"
+ + StringUtils.xmlEscapeQuote(para.getContent()) + "'>"
+ + StringUtils.xmlEscape(para.getContent()) + "</a>");
+ break;
+ }
+ }
+
+ @Override
+ protected void writeParagraphFooter(Paragraph para) throws IOException {
+ switch (para.getType()) {
+ case NORMAL:
+ writer.write("</span>\n");
+ break;
+ case QUOTE:
+ writer.write("</div>\n");
+ break;
+ default:
+ writer.write("\n");
+ break;
+ }
+ }
+
+ @Override
+ protected void writeTextLine(ParagraphType type, String line)
+ throws IOException {
+ switch (type) {
+ case QUOTE:
+ case NORMAL:
+ writer.write(decorateText(StringUtils.xmlEscape(line)));
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected String enbold(String word) {
+ return "<strong>" + word + "</strong>";
+ }
+
+ @Override
+ protected String italize(String word) {
+ return "<emph>" + word + "</emph>";
+ }
}
--- /dev/null
+package be.nikiroo.fanfix.reader;
+
+import java.awt.EventQueue;
+import java.io.File;
+import java.io.IOException;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.Library;
+import be.nikiroo.fanfix.bundles.Config;
+import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.fanfix.output.BasicOutput.OutputType;
+import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
+
+class LocalReader extends BasicReader {
+ private Library lib;
+
+ public LocalReader() throws IOException {
+ File dir = Instance.getReaderDir();
+ dir.mkdirs();
+ if (!dir.exists()) {
+ throw new IOException(
+ "Cannote create cache directory for local reader: " + dir);
+ }
+
+ // TODO: can throw an exception, manage that (convert to IOEx ?)
+ OutputType text = OutputType.valueOfNullOkUC(Instance.getConfig()
+ .getString(Config.LOCAL_READER_NON_IMAGES_DOCUMENT_TYPE));
+ if (text == null) {
+ text = OutputType.HTML;
+ }
+
+ OutputType images = OutputType.valueOfNullOkUC(Instance.getConfig()
+ .getString(Config.LOCAL_READER_IMAGES_DOCUMENT_TYPE));
+ if (images == null) {
+ images = OutputType.CBZ;
+ }
+ //
+
+ lib = new Library(dir, text, images);
+ }
+
+ @Override
+ public void read() throws IOException {
+ }
+
+ @Override
+ public void read(int chapter) {
+ }
+
+ // return new luid
+ public String imprt(String luid) {
+ try {
+ Story story = Instance.getLibrary().getStory(luid);
+ story = lib.save(story);
+ return story.getMeta().getLuid();
+ } catch (IOException e) {
+ Instance.syserr(new IOException(
+ "Cannot import story from library to LocalReader library: "
+ + luid, e));
+ }
+
+ return null;
+ }
+
+ public File getTarget(String luid) {
+ MetaData meta = lib.getInfo(luid);
+ File file = lib.getFile(luid);
+ if (file == null) {
+ luid = imprt(luid);
+ file = lib.getFile(luid);
+ meta = lib.getInfo(luid);
+ }
+
+ return file;
+ }
+
+ @Override
+ public void start(SupportType type) {
+ final SupportType typeFinal = type;
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ new LocalReaderFrame(LocalReader.this, typeFinal)
+ .setVisible(true);
+ }
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ new LocalReader().start(null);
+ }
+}
--- /dev/null
+package be.nikiroo.fanfix.reader;
+
+import java.awt.Desktop;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
+
+class LocalReaderFrame extends JFrame {
+ private static final long serialVersionUID = 1L;
+ private LocalReader reader;
+
+ public LocalReaderFrame(LocalReader reader, SupportType type) {
+ super("HTML reader");
+
+ this.reader = reader;
+
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(800, 600);
+ setLayout(new FlowLayout());
+
+ // TODO: list all stories, list all TMP stories (and format?)
+
+ List<MetaData> stories = Instance.getLibrary().getList(type);
+ for (MetaData story : stories) {
+ JButton button = new JButton(story.getTitle());
+ final String luid = story.getLuid();
+ button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ try {
+ // TODO: config option (image, non image): TXT,
+ // custom-HTML, CBZ, EPUB
+ Desktop.getDesktop().browse(
+ LocalReaderFrame.this.reader.getTarget(luid)
+ .toURI());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ });
+
+ add(button);
+ }
+
+ setVisible(true);
+ }
+}
} finally {
chapIn.close();
}
+
i++;
}
}
line = openDoubleQuote + line + closeDoubleQuote;
newParas.add(new Paragraph(ParagraphType.QUOTE, line));
} else {
+ char open = singleQ ? openQuote : openDoubleQuote;
char close = singleQ ? closeQuote : closeDoubleQuote;
- int posClose = line.indexOf(close, 1);
- int posDot = line.indexOf(".");
- while (posDot >= 0 && posDot < posClose) {
- posDot = line.indexOf(".", posDot + 1);
+
+ int posDot = -1;
+ boolean inQuote = false;
+ int i = 0;
+ for (char car : line.toCharArray()) {
+ if (car == open) {
+ inQuote = true;
+ } else if (car == close) {
+ inQuote = false;
+ } else if (car == '.' && !inQuote) {
+ posDot = i;
+ break;
+ }
+ i++;
}
if (posDot >= 0) {