more changes
[xreader.git] / xreaderfmt.d
blobcef8bd5465015bf01ea680068c3f77f9e9125a67
1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module xreaderfmt;
19 import core.time;
21 import std.concurrency;
23 import iv.strex;
25 import arsd.simpledisplay;
26 import arsd.image;
28 import iv.cmdcon;
29 import iv.nanovg;
30 import iv.nanovg.oui.blendish;
31 import iv.vfs;
32 import iv.vfs.io;
34 import booktext;
36 import xreadercfg;
37 import xlayouter;
40 // ////////////////////////////////////////////////////////////////////////// //
41 BookText loadBook (string fname) {
42 __gshared bool eliteShipsLoaded = false;
44 import std.path;
45 //import core.memory : GC;
46 fname = fname.expandTilde.absolutePath;
47 //conwriteln("loading '", fname, "'...");
48 //GC.disable();
49 auto stt = MonoTime.currTime;
50 auto book = new BookText(fname);
51 //GC.enable();
52 //GC.collect();
53 { conwriteln("loaded: '", fname, "' in ", (MonoTime.currTime-stt).total!"msecs", " milliseconds"); }
55 string[] lines;
56 string[] comments;
57 try {
58 foreach (string line; VFile(buildPath(RcDir, ".lastfile")).byLineCopy) {
59 if (line != fname) {
60 lines ~= line;
61 } else {
62 while (lines.length && lines[$-1].isComment) {
63 comments ~= lines[$-1];
64 lines = lines[0..$-1];
68 } catch (Exception) {}
69 try { import std.file; mkdirRecurse(RcDir); } catch (Exception) {}
70 auto fo = VFile(buildPath(RcDir, ".lastfile"), "w");
71 foreach (string s; lines) fo.writeln(s);
72 foreach_reverse (string s; comments) fo.writeln(s);
73 fo.writeln(fname);
75 stateFileName = buildPath(RcDir, fname.baseName~".rc");
77 if (!eliteShipsLoaded) {
78 loadEliteShips();
79 eliteShipsLoaded = true;
82 return book;
86 // ////////////////////////////////////////////////////////////////////////// //
87 private struct ImageInfo {
88 enum MaxIdleFrames = 5;
89 int iid;
90 TrueColorImage img;
91 int framesNotUsed;
94 private __gshared ImageInfo[] nvgImageIds;
97 void releaseImages (NVGContext vg) {
98 if (nvgImageIds.length) {
99 foreach (ref ImageInfo nfo; nvgImageIds) {
100 if (nfo.iid >= 0) {
101 if (++nfo.framesNotUsed > ImageInfo.MaxIdleFrames) {
102 //conwriteln("freeing image with id ", nfo.iid);
103 vg.deleteImage(nfo.iid);
104 nfo.iid = -1;
105 nfo.img = null;
113 private int registerImage (NVGContext vg, TrueColorImage img) {
114 if (vg is null || img is null) return -1;
115 uint freeIdx = uint.max;
116 foreach (immutable idx, ref ImageInfo nfo; nvgImageIds) {
117 if (nfo.img is img) {
118 assert(nfo.iid >= 0);
119 nfo.framesNotUsed = 0;
120 return nfo.iid;
122 if (freeIdx != uint.max && nfo.iid < 0) freeIdx = cast(uint)idx;
124 int iid = vg.createImageFromMemoryImage(img); //vg.createImageRGBA(img.width, img.height, img.imageData.bytes[], NVGImageFlags.None);
125 if (iid < 0) return -1;
126 nvgImageIds ~= ImageInfo(iid, img, 0);
127 return iid;
131 class BookImage : LayObject {
132 BookText book;
133 uint imageidx;
134 TrueColorImage img;
135 this (BookText abook, uint aidx) { book = abook; imageidx = aidx; img = book.images[aidx].img; }
136 override int width () { return img.width; }
137 override int spacewidth () { return 0; }
138 override int height () { return img.height; }
139 override int ascent () { return img.height; }
140 override int descent () { return 0; }
141 override bool canbreak () { return true; }
142 override bool spaced () { return false; }
143 override void draw (NVGContext vg, float x, float y) {
144 y -= img.height;
145 vg.save();
146 scope(exit) vg.restore();
147 vg.beginPath();
148 int iid = vg.registerImage(img);
149 if (iid < 0) return;
150 vg.fillPaint(vg.imagePattern(x, y, img.width, img.height, 0, iid));
151 //vg.globalAlpha(0.5);
152 vg.rect(x, y, img.width, img.height);
153 vg.fill();
158 // ////////////////////////////////////////////////////////////////////////// //
159 class BookMetadata {
160 static struct Anc {
161 dstring name;
162 uint wordidx; // first word
164 Anc[] sections;
165 Anc[uint] hrefs; // `name` is dest
166 Anc[] ids;
170 // ////////////////////////////////////////////////////////////////////////// //
171 final class FBFormatter {
172 private:
173 BookText book;
174 bool hasSections;
176 public:
177 LayText lay;
178 BookMetadata meta;
180 public:
181 this () {}
183 void formatBook (BookText abook, int maxWidth) {
184 assert(abook !is null);
186 lay = new LayText(laf, maxWidth);
187 lay.fontStyle.color = colorText.asUint;
189 meta = new BookMetadata();
191 book = abook;
192 scope(exit) book = null;
193 hasSections = false;
195 putMain();
197 lay.finalize();
200 private:
201 void dumpTree (Tag ct) {
202 while (ct !is null) {
203 conwriteln("tag: ", ct.name);
204 ct = ct.parent;
208 void badTag (Tag tag, string msg=null) {
209 import std.string : format, indexOf;
210 assert(tag !is null);
211 dumpTree(tag);
212 if (msg.length == 0) msg = "invalid tag: '%s'";
213 if (msg.indexOf("%s") >= 0) {
214 throw new Exception(msg.format(tag.name));
215 } else {
216 throw new Exception(msg);
220 void saveTagId (Tag tag) {
221 if (tag.id.length) {
222 import std.conv : to;
223 meta.ids ~= BookMetadata.Anc(tag.id.to!dstring, lay.nextWordIndex);
227 void registerSection (Tag tag) {
228 import std.conv : to;
229 string text = tag.textContent.xstrip;
230 //lay.sections ~= lay.curWordIndex;
231 string name;
232 while (text.length) {
233 char ch = text.ptr[0];
234 text = text[1..$];
235 if (ch <= ' ' || ch == 127) {
236 if (name.length == 0) continue;
237 if (ch != '\n') ch = ' ';
238 if (ch == '\n') {
239 if (name[$-1] > ' ') name ~= "\n...";
240 text = text.xstrip;
241 } else {
242 if (name[$-1] > ' ') name ~= ' ';
244 } else {
245 name ~= ch;
248 name = xstrip(name);
249 if (name.length == 0) name = "* * *";
250 meta.sections ~= BookMetadata.Anc(name.to!dstring, lay.nextWordIndex);
253 void putImage (Tag tag) {
254 if (tag is null || tag.href.length < 2 || tag.href[0] != '#') return;
255 string iid = tag.href[1..$];
256 //conwriteln("searching for image with href '", iid, "' (", book.images.length, ")");
257 foreach (immutable idx, ref BookText.Image img; book.images) {
258 if (iid == img.id) {
259 //conwriteln("image '", img.id, "' found");
260 lay.putObject(new BookImage(book, cast(uint)idx));
261 return;
266 void putParasXX(bool allowBad=false) (Tag tag) {
267 if (tag is null || tag.name.length == 0) return;
268 switch (tag.name) {
269 case "p":
270 putTagContents(tag);
271 break;
272 case "empty-line":
273 lay.endLine();
274 break;
275 case "br":
276 lay.endPara();
277 break;
278 case "text-author":
279 break;
280 case "strong":
281 putTagContents(tag, 1);
282 break;
283 case "poem":
284 putPoem(tag);
285 lay.put(LayText.EndParaCh);
286 break;
287 case "subtitle":
288 //FIXME:??? somehow this is invisible
289 lay.pushStyles();
290 scope(exit) lay.popStyles;
291 setSubtitleStyle();
292 if (tag.text.length) { putParaContentInternal(tag); lay.endPara(); } else putTagContents(tag);
293 break;
294 case "sub":
295 case "sup":
296 putTagContents(tag); // some idiotic files has this
297 break;
298 case "site":
299 case "annotation":
300 putParaContentInternal(tag); // some idiotic files has this
301 break;
302 case "epigraph":
303 lay.pushStyles();
304 scope(exit) lay.popStyles;
305 //auto olpad = lay.lineStyle.leftpad;
306 auto orpad = lay.lineStyle.rightpad;
307 setEpigraphStyle();
308 //lay.lineStyle.leftpad = lay.lineStyle.leftpad+olpad;
309 lay.lineStyle.rightpad = lay.lineStyle.rightpad+orpad;
310 putEpigraph(tag, false);
311 break;
312 case "emphasis":
313 lay.pushStyles();
314 scope(exit) lay.popStyles;
315 lay.fontStyle.italic = true;
316 putEpigraph(tag, false);
317 break;
318 default:
319 static if (allowBad) {
320 break;
321 } else {
322 badTag(tag);
327 void putParaContentInternal (Tag ct, int boldc=0, int italicc=0, int underc=0) {
328 if (ct is null) return;
329 auto c = lay.fontStyle.color;
330 scope(exit) lay.fontStyle.color = c;
331 bool popStyles = false;
332 bool doFixFont = true;
333 scope(exit) {
334 if (popStyles) lay.popStyles;
335 fixFont();
337 saveTagId(ct);
338 switch (ct.name) {
339 case "strong": ++boldc; lay.fontStyle.bold = true; break;
340 case "emphasis": ++italicc; lay.fontStyle.italic = true; break;
341 case "strikethrough": lay.pushStyles(); popStyles = true; lay.fontStyle.strike = true; break;
342 case "image": putImage(ct); return;
343 case "style": return;
344 case "a":
345 if (ct.href.length) {
346 import std.conv : to;
347 //conwriteln("href found: '", ct.href, "' (", lay.nextWordIndex, ")");
348 meta.hrefs[lay.nextWordIndex] = BookMetadata.Anc(ct.href.to!dstring, lay.nextWordIndex);
349 ++underc;
350 lay.fontStyle.href = true;
351 lay.fontStyle.underline = true;
352 lay.fontStyle.color = colorTextHref.asUint;
354 break;
355 case "sub": break; // some idiotic files has this
356 case "sup": break; // some idiotic files has this
357 case "p":
358 putTagContents(ct, boldc, italicc, underc);
359 fixFont();
360 return;
361 case "br":
362 //lay.endLine();
363 lay.endPara();
364 return;
365 case "empty-line":
366 lay.endLine();
367 return;
368 case "code":
369 lay.pushStyles();
370 popStyles = true;
371 setCodeStyle();
372 doFixFont = false;
373 break;
374 case "site":
375 case "annotation":
376 lay.pushStyles();
377 popStyles = true;
378 ++boldc;
379 lay.fontStyle.bold = true;
380 lay.lineStyle.setCenter;
381 break;
382 case "poem":
383 lay.pushStyles();
384 popStyles = true;
385 putPoem(ct);
386 lay.put(LayText.EndParaCh);
387 return;
388 case "":
389 lay.put(ct.text);
390 return;
391 default:
392 badTag(ct);
393 break;
395 if (doFixFont) fixFont();
396 foreach (Tag tag; ct.children) putParaContentInternal(tag, boldc, italicc, underc);
397 switch (ct.name) {
398 case "strong": case "site": case "annotation": if (--boldc == 0) lay.fontStyle.bold = false; break;
399 case "emphasis": if (--italicc == 0) lay.fontStyle.italic = false; break;
400 case "a": if (--underc == 0) lay.fontStyle.underline = false; lay.fontStyle.href = false; break;
401 default:
405 bool onlyImagePara (Tag ct) {
406 if (ct is null) return false;
407 int count;
408 foreach (Tag tag; ct.children) {
409 if (tag.name.length == 0) {
410 // text
411 if (tag.text.xstrip.length != 0) return false;
412 continue;
414 if (count != 0 || tag.name != "image") return false;
415 ++count;
417 return (count == 1);
420 void putTagContents/*(bool xdump=false)*/ (Tag ct, int boldc=0, int italicc=0, int underc=0) {
421 if (ct is null) return;
422 saveTagId(ct);
423 if (onlyImagePara(ct)) {
424 lay.pushStyles();
425 scope(exit) lay.popStyles;
426 lay.lineStyle.setCenter;
427 foreach (Tag tag; ct.children) putParaContentInternal(tag, boldc, italicc, underc);
428 } else {
429 //if (ct.name == "subtitle") conwriteln("text: [", ct.children[0].text, "]");
430 foreach (Tag tag; ct.children) {
431 //static if (xdump) conwriteln("text: [", tag.text, "]");
432 putParaContentInternal(tag, boldc, italicc, underc);
435 lay.endPara();
438 void putParas (Tag ct) {
439 saveTagId(ct);
440 foreach (Tag tag; ct.children) {
441 putParasXX(tag);
445 void putAuthor (Tag ct) {
446 saveTagId(ct);
447 foreach (Tag tag; ct.children) {
448 if (tag.name.length == 0) continue;
449 if (tag.name == "text-author") {
450 putTagContents(tag);
451 lay.endPara();
456 void putCite (Tag ct) {
457 lay.pushStyles();
458 scope(exit) lay.popStyles;
459 setCiteStyle();
460 putParas(ct);
461 putAuthor(ct);
464 void putEpigraph (Tag ct, bool setstyle=true) {
465 if (setstyle) lay.pushStyles();
466 scope(exit) if (setstyle) lay.popStyles;
467 if (setstyle) setEpigraphStyle();
468 putParas(ct);
469 putAuthor(ct);
472 void putStanza (Tag ct) {
473 saveTagId(ct);
474 foreach (Tag tag; ct.children) {
475 if (tag.name.length == 0) continue;
476 if (tag.name == "text-author") continue;
477 if (tag.name == "title") badTag(tag, "titles in stanzas are not supported yet");
478 if (tag.name == "subtitle") badTag(tag, "subtitles in stanzas are not supported yet");
479 if (tag.name == "epigraph") badTag(tag, "epigraphs in poems are not supported yet");
480 if (tag.name == "date") continue;
481 if (tag.name == "v") { putTagContents(tag); continue; }
482 badTag(tag);
486 void putPoem (Tag ct) {
487 saveTagId(ct);
488 lay.pushStyles();
489 scope(exit) lay.popStyles;
490 setPoemStyle();
491 // put epigraph (not yet)
492 // put title and subtitle
493 foreach (Tag tag; ct.children) {
494 if (tag.name == "title") {
495 lay.pushStyles();
496 scope(exit) lay.popStyles;
497 if (!hasSections) registerSection(tag);
498 putParas(tag);
499 lay.endPara(); // space
500 } else if (tag.name == "subtitle") {
501 lay.pushStyles();
502 scope(exit) lay.popStyles;
503 putParas(tag);
504 //lay.endPara(); // space
507 lay.endPara;
508 foreach (Tag tag; ct.children) {
509 if (tag.name.length == 0) continue;
510 if (tag.name == "text-author") continue;
511 if (tag.name == "title") continue;
512 if (tag.name == "subtitle") continue;
513 if (tag.name == "epigraph") badTag(tag, "epigraphs in poems are not supported yet");
514 if (tag.name == "date") continue;
515 if (tag.name == "stanza") { putStanza(tag); lay.put(LayText.EndParaCh); continue; }
516 badTag(tag);
518 putAuthor(ct);
521 void putSection (Tag sc) {
522 bool sectionRegistered = false;
523 hasSections = true;
525 saveTagId(sc);
526 foreach (Tag tag; sc.children) {
527 if (tag.name.length == 0) continue;
528 if (tag.name == "title") {
529 lay.pushStyles();
530 scope(exit) lay.popStyles;
531 setTitleStyle();
532 if (!sectionRegistered) { sectionRegistered = true; registerSection(tag); }
533 putParas(tag);
534 } else if (tag.name == "subtitle") {
535 lay.pushStyles();
536 scope(exit) lay.popStyles;
537 setSubtitleStyle();
538 if (!sectionRegistered) { sectionRegistered = true; registerSection(tag); }
539 putParas(tag);
540 } else if (tag.name == "epigraph") {
541 putEpigraph(tag);
542 } else if (tag.name == "section") {
543 lay.pushStyles();
544 scope(exit) lay.popStyles;
545 setNormalStyle();
546 putSection(tag);
547 } else if (tag.name == "p") {
548 putTagContents(tag);
549 } else if (tag.name == "image") {
550 lay.pushStyles();
551 scope(exit) lay.popStyles;
552 lay.lineStyle.leftpad = 0;
553 lay.lineStyle.rightpad = 0;
554 lay.lineStyle.paraIndent = 0;
555 lay.lineStyle.setCenter;
556 putImage(tag);
557 lay.endPara();
558 } else if (tag.name == "empty-line") {
559 lay.endPara();
560 } else if (tag.name == "cite") {
561 putCite(tag);
562 } else if (tag.name == "table") {
563 lay.pushStyles();
564 scope(exit) lay.popStyles;
565 lay.fontStyle.fontsize += 8;
566 lay.lineStyle.setCenter;
567 lay.put("TABLE SKIPPED");
568 lay.endPara();
569 } else if (tag.name == "poem") {
570 putPoem(tag);
571 } else if (tag.name == "image") {
572 } else if (tag.name == "style") {
573 } else if (tag.name == "site" || tag.name == "annotation") {
574 } else {
575 badTag(tag);
578 lay.endPara;
581 void putMain () {
582 foreach (Tag tag; book.content.children) {
583 if (tag.name == "title") {
584 lay.pushStyles();
585 scope(exit) lay.popStyles;
586 setTitleStyle();
587 lay.endPara();
588 if (!hasSections) registerSection(tag);
589 putParas(tag);
590 lay.endPara();
591 } else if (tag.name == "subtitle") {
592 lay.pushStyles();
593 scope(exit) lay.popStyles;
594 setTitleStyle();
595 putParas(tag);
596 lay.endPara();
597 } else if (tag.name == "epigraph") {
598 putEpigraph(tag);
599 } else if (tag.name == "section") {
600 lay.pushStyles();
601 scope(exit) lay.popStyles;
602 setNormalStyle();
603 putSection(tag);
604 } else if (tag.name == "image") {
605 putImage(tag);
606 } else /*if (tag.name == "empty-line") {
607 lay.endLine();
608 } else if (tag.name == "p") {
609 lay.pushStyles();
610 scope(exit) lay.popStyles;
611 setNormalStyle(lay);
612 putParas(tag);
613 lay.endPara();
614 putTagContents(tag);
615 }*/{
616 lay.pushStyles();
617 scope(exit) lay.popStyles;
618 setNormalStyle();
619 putParasXX!true(tag);
624 private:
625 void fixFont () {
626 if (lay.fontStyle.italic) {
627 if (lay.fontStyle.bold) lay.fontStyle.fontface = lay.fontFaceId("textz");
628 else lay.fontStyle.fontface = lay.fontFaceId("texti");
629 } else if (lay.fontStyle.bold) {
630 if (lay.fontStyle.italic) lay.fontStyle.fontface = lay.fontFaceId("textz");
631 else lay.fontStyle.fontface = lay.fontFaceId("textb");
632 } else {
633 lay.fontStyle.fontface = lay.fontFaceId("text");
637 void setNormalStyle () {
638 lay.fontStyle.resetAttrs;
639 lay.fontStyle.fontsize = fsizeText;
640 lay.fontStyle.color = colorText.asUint;
641 lay.lineStyle.leftpad = 0;
642 lay.lineStyle.rightpad = 0;
643 lay.lineStyle.paraIndent = 3;
644 if (optJustify) lay.lineStyle.setJustify; else lay.lineStyle.setLeft;
645 fixFont();
648 void setTitleStyle () {
649 lay.fontStyle.resetAttrs;
650 lay.fontStyle.bold = true;
651 lay.fontStyle.fontsize = fsizeText+4;
652 lay.fontStyle.color = colorText.asUint;
653 lay.lineStyle.leftpad = 0;
654 lay.lineStyle.rightpad = 0;
655 lay.lineStyle.paraIndent = 0;
656 lay.lineStyle.setCenter;
657 fixFont();
660 void setSubtitleStyle () {
661 lay.fontStyle.resetAttrs;
662 lay.fontStyle.bold = true;
663 lay.fontStyle.fontsize = fsizeText+2;
664 lay.fontStyle.color = colorText.asUint;
665 lay.lineStyle.leftpad = 0;
666 lay.lineStyle.rightpad = 0;
667 lay.lineStyle.paraIndent = 0;
668 lay.lineStyle.setCenter;
669 fixFont();
672 void setCiteStyle () {
673 lay.fontStyle.resetAttrs;
674 lay.fontStyle.italic = true;
675 lay.fontStyle.fontsize = fsizeText;
676 lay.fontStyle.color = colorText.asUint;
677 lay.lineStyle.leftpad = fsizeText*3;
678 lay.lineStyle.rightpad = fsizeText*3;
679 lay.lineStyle.paraIndent = 3;
680 if (optJustify) lay.lineStyle.setJustify; else lay.lineStyle.setLeft;
681 fixFont();
684 void setEpigraphStyle () {
685 lay.fontStyle.resetAttrs;
686 lay.fontStyle.italic = true;
687 lay.fontStyle.fontsize = fsizeText;
688 lay.fontStyle.color = colorText.asUint;
689 lay.lineStyle.leftpad = lay.width/3;
690 lay.lineStyle.rightpad = 0;
691 lay.lineStyle.paraIndent = 0;
692 lay.lineStyle.setRight;
693 fixFont();
696 void setPoemStyle () {
697 lay.fontStyle.resetAttrs;
698 lay.fontStyle.italic = true;
699 lay.fontStyle.fontsize = fsizeText;
700 lay.fontStyle.color = colorText.asUint;
701 lay.lineStyle.leftpad = fsizeText*3;
702 lay.lineStyle.rightpad = 0;
703 lay.lineStyle.paraIndent = 0;
704 lay.lineStyle.setLeft;
705 fixFont();
708 void setCodeStyle () {
709 lay.fontStyle.resetAttrs;
710 lay.fontStyle.italic = false;
711 lay.fontStyle.fontsize = fsizeText;
712 lay.fontStyle.color = colorText.asUint;
713 lay.lineStyle.leftpad = fsizeText*3;
714 lay.lineStyle.rightpad = fsizeText*3;
715 lay.lineStyle.paraIndent = 0;
716 lay.lineStyle.setLeft;
717 lay.fontStyle.fontface = lay.fontFaceId("mono");
722 // ////////////////////////////////////////////////////////////////////////// //
723 private __gshared LayFontStash laf; // layouter font stash
726 // ////////////////////////////////////////////////////////////////////////// //
727 private void loadFmtFonts () {
728 laf = new LayFontStash();
730 laf.addFont("text", textFontName);
731 laf.addFont("texti", textiFontName);
732 laf.addFont("textb", textbFontName);
733 laf.addFont("textz", textzFontName);
735 laf.addFont("mono", monoFontName);
736 laf.addFont("monoi", monoiFontName);
737 laf.addFont("monob", monobFontName);
738 laf.addFont("monoz", monozFontName);
742 // ////////////////////////////////////////////////////////////////////////// //
743 // messages
744 struct ReformatWork {
745 shared(BookText) booktext;
746 string bookFileName;
747 int w, h;
750 struct ReformatWorkComplete {
751 int w, h;
752 shared(BookText) booktext;
753 shared(LayText) laytext;
754 shared(BookMetadata) meta;
757 struct QuitWork {
761 // ////////////////////////////////////////////////////////////////////////// //
762 void reformatThreadFn (Tid ownerTid) {
763 bool doQuit = false;
764 loadFmtFonts();
765 BookText book;
766 string newFileName;
767 int newW = -1, newH = -1;
768 while (!doQuit) {
769 book = null;
770 newFileName = null;
771 newW = newH = -1;
772 receive(
773 (ReformatWork w) {
774 //{ conwriteln("reformat request received..."); }
775 book = cast(BookText)w.booktext;
776 newFileName = w.bookFileName;
777 newW = w.w;
778 newH = w.h;
779 if (newW < 1) newW = 1;
780 if (newH < 1) newH = 1;
782 (QuitWork w) {
783 doQuit = true;
786 if (!doQuit && newW > 0 && newH > 0) {
787 try {
788 if (book is null) {
789 //conwriteln("loading new book: '", newFileName, "'");
790 book = loadBook(newFileName);
793 int maxWidth = newW-4-2-BND_SCROLLBAR_WIDTH-2;
794 if (maxWidth < 64) maxWidth = 64;
796 // layout text
797 //conwriteln("layouting...");
798 auto fmtr = new FBFormatter();
800 auto stt = MonoTime.currTime;
802 auto lay = new LayText(laf, maxWidth);
803 lay.fontStyle.color = colorText.asUint;
804 auto meta = book.formatBook(lay);
806 fmtr.formatBook(book, maxWidth);
807 auto ett = MonoTime.currTime-stt;
809 auto meta = fmtr.meta;
810 auto lay = fmtr.lay;
812 conwriteln("layouted in ", ett.total!"msecs", " milliseconds");
813 auto res = ReformatWorkComplete(newW, newH, cast(shared)book, cast(shared)lay, cast(shared)meta);
814 send(ownerTid, res);
815 } catch (Throwable e) {
816 // here, we are dead and fucked (the exact order doesn't matter)
817 import core.stdc.stdlib : abort;
818 import core.stdc.stdio : fprintf, stderr;
819 import core.memory : GC;
820 import core.thread : thread_suspendAll;
821 GC.disable(); // yeah
822 thread_suspendAll(); // stop right here, you criminal scum!
823 auto s = e.toString();
824 fprintf(stderr, "\n=== FATAL ===\n%.*s\n", cast(uint)s.length, s.ptr);
825 abort(); // die, you bitch!
829 send(ownerTid, QuitWork());