1 package be
.nikiroo
.fanfix
.output
;
4 import java
.io
.IOException
;
5 import java
.util
.ArrayList
;
8 import be
.nikiroo
.fanfix
.Instance
;
9 import be
.nikiroo
.fanfix
.bundles
.StringId
;
10 import be
.nikiroo
.fanfix
.data
.Chapter
;
11 import be
.nikiroo
.fanfix
.data
.Paragraph
;
12 import be
.nikiroo
.fanfix
.data
.Paragraph
.ParagraphType
;
13 import be
.nikiroo
.fanfix
.data
.Story
;
14 import be
.nikiroo
.utils
.Progress
;
17 * This class is the base class used by the other output classes. It can be used
18 * outside of this package, and have static method that you can use to get
19 * access to the correct support class.
23 public abstract class BasicOutput
{
25 * The supported output types for which we can get a {@link BasicOutput}
30 public enum OutputType
{
31 /** EPUB files created with this program */
33 /** Pure text file with some rules */
35 /** TEXT but with associated .info file */
37 /** DEBUG output to console */
39 /** ZIP with (PNG) images */
41 /** LaTeX file with "book" template */
43 /** HTML files in a dedicated directory */
49 public String
toString() {
50 return super.toString().toLowerCase();
54 * A description of this output type.
57 * TRUE for the long description, FALSE for the short one
59 * @return the description
61 public String
getDesc(boolean longDesc
) {
62 StringId id
= longDesc ? StringId
.OUTPUT_DESC
63 : StringId
.OUTPUT_DESC_SHORT
;
65 String desc
= Instance
.getTrans().getStringX(id
, this.name());
68 desc
= Instance
.getTrans().getString(id
, this);
72 desc
= this.toString();
79 * The default extension to add to the output files.
82 * the target to point to to read the {@link Story} (for
83 * instance, the main entry point if this {@link Story} is in
86 * @return the extension
88 public String
getDefaultExtension(boolean readerTarget
) {
89 BasicOutput output
= BasicOutput
.getOutput(this, false);
91 return output
.getDefaultExtension(readerTarget
);
98 * Call {@link OutputType#valueOf(String)} after conversion to upper
102 * the possible type name
104 * @return NULL or the type
106 public static OutputType
valueOfUC(String typeName
) {
107 return OutputType
.valueOf(typeName
== null ?
null : typeName
112 * Call {@link OutputType#valueOf(String)} after conversion to upper
113 * case but return def for NULL and empty instead of raising an
117 * the possible type name
121 * @return NULL or the type
123 public static OutputType
valueOfNullOkUC(String typeName
, OutputType def
) {
124 if (typeName
== null || typeName
.isEmpty()) {
128 return OutputType
.valueOfUC(typeName
);
132 * Call {@link OutputType#valueOf(String)} after conversion to upper
133 * case but return def in case of error instead of raising an exception.
136 * the possible type name
140 * @return NULL or the type
142 public static OutputType
valueOfAllOkUC(String typeName
, OutputType def
) {
144 return OutputType
.valueOfUC(typeName
);
145 } catch (Exception e
) {
151 /** The creator name (this program, by me!) */
152 static final String EPUB_CREATOR
= "Fanfix (by Niki)";
154 /** The current best name for an image */
155 private String imageName
;
156 private File targetDir
;
157 private String targetName
;
158 private OutputType type
;
159 private boolean writeCover
;
160 private boolean writeInfo
;
161 private Progress storyPg
;
162 private Progress chapPg
;
165 * Process the {@link Story} into the given target.
168 * the {@link Story} to export
170 * the target where to save to (will not necessary be taken as is
171 * by the processor, for instance an extension can be added)
173 * the optional progress reporter
175 * @return the actual main target saved, which can be slightly different
178 * @throws IOException
179 * in case of I/O error
181 public File
process(Story story
, String target
, Progress pg
)
185 File targetDir
= null;
186 String targetName
= null;
187 if (target
!= null) {
188 target
= new File(target
).getAbsolutePath();
189 targetDir
= new File(target
).getParentFile();
190 targetName
= new File(target
).getName();
192 String ext
= getDefaultExtension(false);
193 if (ext
!= null && !ext
.isEmpty()) {
194 if (targetName
.toLowerCase().endsWith(ext
)) {
195 targetName
= targetName
.substring(0, targetName
.length()
201 return process(story
, targetDir
, targetName
);
205 * Process the {@link Story} into the given target.
207 * This method is expected to be overridden in most cases.
210 * the {@link Story} to export
212 * the target dir where to save to
214 * the target filename (will not necessary be taken as is by the
215 * processor, for instance an extension can be added)
218 * @return the actual main target saved, which can be slightly different
221 * @throws IOException
222 * in case of I/O error
224 protected File
process(Story story
, File targetDir
, String targetName
)
226 this.targetDir
= targetDir
;
227 this.targetName
= targetName
;
239 public OutputType
getType() {
244 * Enable the creation of a .info file next to the resulting processed file.
246 * @return TRUE to enable it
248 protected boolean isWriteInfo() {
253 * Enable the creation of a cover file next to the resulting processed file
256 * @return TRUE to enable it
258 protected boolean isWriteCover() {
268 * TRUE to enable the creation of a .info file
270 * TRUE to enable the creation of a cover if possible
274 protected BasicOutput
setType(OutputType type
, boolean writeCover
,
277 this.writeCover
= writeCover
;
278 this.writeInfo
= writeInfo
;
284 * The default extension to add to the output files.
286 * @param readerTarget
287 * the target to point to to read the {@link Story} (for
288 * instance, the main entry point if this {@link Story} is in a
291 * @return the extension
293 public String
getDefaultExtension(
294 @SuppressWarnings("unused") boolean readerTarget
) {
298 @SuppressWarnings("unused")
299 protected void writeStoryHeader(Story story
) throws IOException
{
302 @SuppressWarnings("unused")
303 protected void writeChapterHeader(Chapter chap
) throws IOException
{
306 @SuppressWarnings("unused")
307 protected void writeParagraphHeader(Paragraph para
) throws IOException
{
310 @SuppressWarnings("unused")
311 protected void writeStoryFooter(Story story
) throws IOException
{
314 @SuppressWarnings("unused")
315 protected void writeChapterFooter(Chapter chap
) throws IOException
{
318 @SuppressWarnings("unused")
319 protected void writeParagraphFooter(Paragraph para
) throws IOException
{
322 protected void writeStory(Story story
) throws IOException
{
323 if (storyPg
== null) {
324 storyPg
= new Progress(0, story
.getChapters().size() + 2);
326 storyPg
.setMinMax(0, story
.getChapters().size() + 2);
329 String chapterNameNum
= String
.format("%03d", 0);
330 String paragraphNumber
= String
.format("%04d", 0);
331 imageName
= paragraphNumber
+ "_" + chapterNameNum
+ ".png";
333 if (story
.getMeta() != null) {
334 story
.getMeta().setType("" + getType());
338 InfoCover
.writeCover(targetDir
, targetName
, story
.getMeta());
341 InfoCover
.writeInfo(targetDir
, targetName
, story
.getMeta());
344 storyPg
.setProgress(1);
346 List
<Progress
> chapPgs
= new ArrayList
<Progress
>(story
.getChapters()
348 for (Chapter chap
: story
) {
349 chapPg
= new Progress(0, chap
.getParagraphs().size());
350 storyPg
.addProgress(chapPg
, 1);
355 writeStoryHeader(story
);
356 for (int i
= 0; i
< story
.getChapters().size(); i
++) {
357 chapPg
= chapPgs
.get(i
);
358 writeChapter(story
.getChapters().get(i
));
359 chapPg
.setProgress(chapPg
.getMax());
362 writeStoryFooter(story
);
364 storyPg
.setProgress(storyPg
.getMax());
368 protected void writeChapter(Chapter chap
) throws IOException
{
369 String chapterNameNum
;
370 if (chap
.getName() == null || chap
.getName().isEmpty()) {
371 chapterNameNum
= String
.format("%03d", chap
.getNumber());
373 chapterNameNum
= String
.format("%03d", chap
.getNumber()) + "_"
374 + chap
.getName().replace(" ", "_");
378 String paragraphNumber
= String
.format("%04d", num
++);
379 imageName
= chapterNameNum
+ "_" + paragraphNumber
+ ".png";
381 writeChapterHeader(chap
);
383 for (Paragraph para
: chap
) {
384 paragraphNumber
= String
.format("%04d", num
++);
385 imageName
= chapterNameNum
+ "_" + paragraphNumber
+ ".png";
386 writeParagraph(para
);
387 if (chapPg
!= null) {
388 chapPg
.setProgress(i
++);
391 writeChapterFooter(chap
);
394 protected void writeParagraph(Paragraph para
) throws IOException
{
395 writeParagraphHeader(para
);
396 writeTextLine(para
.getType(), para
.getContent());
397 writeParagraphFooter(para
);
400 @SuppressWarnings("unused")
401 protected void writeTextLine(ParagraphType type
, String line
)
406 * Return the current best guess for an image name, based upon the current
407 * {@link Chapter} and {@link Paragraph}.
410 * add the original target name as a prefix
412 * @return the guessed name
414 protected String
getCurrentImageBestName(boolean prefix
) {
416 return targetName
+ "_" + imageName
;
423 * Return the given word or sentence as <b>bold</b>.
428 * @return the bold output
430 protected String
enbold(String word
) {
435 * Return the given word or sentence as <i>italic</i>.
440 * @return the italic output
442 protected String
italize(String word
) {
447 * Decorate the given text with <b>bold</b> and <i>italic</i> words,
448 * according to {@link BasicOutput#enbold(String)} and
449 * {@link BasicOutput#italize(String)}.
454 * @return the decorated output
456 protected String
decorateText(String text
) {
457 StringBuilder builder
= new StringBuilder();
462 for (char car
: text
.toCharArray()) {
465 if (bold
>= 0 && prev
!= ' ') {
466 String data
= builder
.substring(bold
);
467 builder
.setLength(bold
);
468 builder
.append(enbold(data
));
471 && (prev
== ' ' || prev
== '\0' || prev
== '\n')) {
472 bold
= builder
.length();
479 if (italic
>= 0 && prev
!= ' ') {
480 String data
= builder
.substring(italic
);
481 builder
.setLength(italic
);
482 builder
.append(enbold(data
));
484 } else if (italic
< 0
485 && (prev
== ' ' || prev
== '\0' || prev
== '\n')) {
486 italic
= builder
.length();
501 builder
.insert(bold
, '*');
505 builder
.insert(italic
, '_');
508 return builder
.toString();
512 * Return a {@link BasicOutput} object compatible with the given
513 * {@link OutputType}.
518 * force the <tt>.info</tt> file and the cover to be saved next
519 * to the main target file
521 * @return the {@link BasicOutput}
523 public static BasicOutput
getOutput(OutputType type
, boolean infoCover
) {
527 return new Epub().setType(type
, infoCover
, infoCover
);
529 return new Text().setType(type
, true, infoCover
);
531 return new InfoText().setType(type
, true, true);
533 return new Sysout().setType(type
, false, false);
535 return new Cbz().setType(type
, infoCover
, infoCover
);
537 return new LaTeX().setType(type
, infoCover
, infoCover
);
539 return new Html().setType(type
, infoCover
, infoCover
);