#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool _print_aborted = false; /////////////////////////////////////////////////////////// // Utility /////////////////////////////////////////////////////////// static void advanced_draw_justified_text_line(WINDOW w, const char* text, short x, short y, short dx) { TString256 txt(text); txt.rtrim(); int spaces = 0; for (int s = 0; txt[s]; s++) if (isspace(txt[s])) spaces++; // Il testo e' giustificabile se ha degli spazi ed occupa meno della riga if (spaces > 0) { const int tw = xvt_dwin_get_text_width(w, txt, -1); if (tw < dx) { txt << ' '; // Aggiunge spazio finale const double kspc = double(dx - tw) / spaces; int start = 0; double kx = x; for (int i = 0; txt[i]; i++) if (isspace(txt[i])) { const bool last_word = txt[i+1] == '\0'; const TString& parola = txt.sub(start, i + (last_word ? 0 : 1)); const int lw = xvt_dwin_get_text_width(w, parola, -1); if (last_word) // ultima parola kx = x+dx-lw; xvt_dwin_draw_text(w, int(kx+0.5), y, parola, -1); kx += lw + kspc; start = i+1; } } else xvt_dwin_draw_text(w, x, y, txt, -1); // Stringa che deborda dalla riga } else xvt_dwin_draw_text(w, x, y, txt, -1); // Stringa senza spazi } void advanced_draw_text_line(WINDOW w, const char* text, const RCT& r, char halign, char valign) { const short dx = r.right-r.left; const short dy = r.bottom-r.top; short x = r.left; short y = r.bottom; if (halign != 'L') { const int tw = xvt_dwin_get_text_width(w, text, -1); switch (halign) { case 'C': x += (dx - tw)/2; break; case 'R': x = r.right-tw-1; break; default : break; } } // Text Height int leading, ascent, descent; xvt_dwin_get_font_metrics(w, &leading, &ascent, &descent); switch (valign) { case 'C': y -= (dy - ascent-descent)/2; break; case 'T': y = r.top + ascent; break; default : y -= descent+leading; break; } bool can_draw = true; RCT orig; xvt_dwin_get_clip(w, &orig); bool restore_clip = !xvt_rect_is_empty(&orig); if (restore_clip) { RCT clipper; can_draw = xvt_rect_intersect(&clipper, &orig, &r) != 0; if (can_draw) xvt_dwin_set_clip(w, &clipper); else restore_clip = false; } else xvt_dwin_set_clip(w, &r); if (can_draw) { if (halign == 'J') advanced_draw_justified_text_line(w, text, x, y, dx); else xvt_dwin_draw_text(w, x, y, text, -1); if (restore_clip) xvt_dwin_set_clip(w, &orig); } } bool finisce_per_punto(const TString& str) { bool yes = false; const int len = str.len(); if (len > 0) { const char last = str[len-1]; yes = strchr(".!?:", last) != NULL; } return yes; } void advanced_draw_paragraph(WINDOW win, const TString_array& para, const RCT& rct, char halign, char valign, int default_10row_height) { const int needed_rows = para.items(); if (needed_rows > 1) // Devo scrivere piu' righe? { int leading, ascent, descent; xvt_dwin_get_font_metrics(win, &leading, &ascent, &descent); const int rct_height = rct.bottom - rct.top; const int row_height = leading + ascent + descent; int ky10 = row_height * 10; // Aggiusta l'altezza di una riga standard, se necessario if (ky10 < default_10row_height && ky10 > 75*default_10row_height/100) { const int avail_rows = (rct_height+row_height/4)/row_height; if (avail_rows > needed_rows) ky10 = default_10row_height; else if (avail_rows >= 4 && avail_rows >= needed_rows-1) ky10 = rct_height*10/needed_rows; } int ybase = rct.top; switch (valign) { case 'C': ybase += (rct_height - (needed_rows * ky10) / 10) / 2; break; case 'B': ybase += rct_height - (needed_rows * ky10) / 10; break; default : break; } const int lastop = rct.bottom - ky10/20; // Ultima y valida = base del rettangolo MENO mezza riga for (int row = 0; row < needed_rows; row++) { const int top = ybase + (ky10 * row) / 10; if (top < lastop) { const TString& line = para.row(row); if (line.full()) { RCT rctline = rct; rctline.top = top; rctline.bottom = ybase + (ky10 * (row+1)) / 10; // top + ky10 / 10; char ha = halign; if (ha == 'J' && (row == needed_rows-1 || finisce_per_punto(line))) ha = 'L'; // Le righe finali non vanno giustificate advanced_draw_text_line(win, line, rctline, ha, 'T'); } } else break; } } else { if (halign == 'J') halign = 'L'; // Le righe isolate non vanno giustificate advanced_draw_text_line(win, para.row(0), rct, halign, valign); } } /////////////////////////////////////////////////////////// // TReport_link /////////////////////////////////////////////////////////// void TReport_link::set(const char* field, const TString& var) { _fields.add(field, var, true); } const TString& TReport_link::get(const char* field) const { const TString* var = (const TString*)_fields.objptr(field); return var != NULL ? *var : (const TString&)EMPTY_STRING; } void TReport_link::add_rect(const RCT& rct) { // Non memorizzo tutti i rettangoli del link, ma la loro unione if (xvt_rect_is_empty(&_rct)) _rct = rct; else { if (rct.left < _rct.left) _rct.left = rct.left; if (rct.top < _rct.top) _rct.top = rct.top; if (rct.right > _rct.right) _rct.right = rct.right; if (rct.bottom > _rct.bottom) _rct.bottom = rct.bottom; } } int TReport_link::hit_test(const PNT& p) const { if (p.v < _rct.top) return -1; if (p.v > _rct.bottom) return +1; if (p.h < _rct.left) return -1; if (p.h > _rct.right) return +1; return 0; } int TReport_link::compare(const TSortable& s) const { const TReport_link& lnk = (const TReport_link&)s; int cmp = _rct.top - lnk._rct.top; if (cmp == 0) cmp = _rct.left - lnk._rct.left; return cmp; } TReport_link::TReport_link(const char* table) : _table(table) { xvt_rect_set_empty(&_rct); } /////////////////////////////////////////////////////////// // TPrint_preview_window /////////////////////////////////////////////////////////// class TPrint_preview_window : public TField_window { TBook* _book; word _page; int _zoom; bool _grid; TAssoc_array _alinks; TPointer_array _plinks; protected: void page_select(); void popup_menu(EVENT* ep); const TReport_link* find_link(const PNT& pnt) const; void draw_page(int pg); virtual long handler(WINDOW win, EVENT* ep); virtual void update(); virtual bool on_key(KEY k); void do_scroll(int kx, int ky); public: virtual PNT log2dev(long lx, long ly) const; void update_scroll_range(); short book2pix(int size) const; TAssoc_array& alinks() { return _alinks; } TPointer_array& plinks() { return _plinks; } TPrint_preview_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner, TBook* book); virtual ~TPrint_preview_window(); }; short TPrint_preview_window::book2pix(int sz) const { return short(sz * _zoom / BOOKDPI); } PNT TPrint_preview_window::log2dev(long lx, long ly) const { PNT pnt = { book2pix(ly), book2pix(lx) }; const TPoint orig = origin(); pnt.h -= short(orig.x); pnt.v -= short(orig.y); return pnt; } void TPrint_preview_window::update_scroll_range() { update_thumb(0, 0); // Azzero origine const TSize pag = _book->page_size(); const PNT pnt = log2dev(pag.x, pag.y); RCT rct; xvt_vobj_get_client_rect(win(), &rct); const PNT siz = { rct.bottom, rct.right }; set_scroll_max(pnt.h, pnt.v); set_thumb_size(siz.h, siz.v); } void TPrint_preview_window::draw_page(int pg) { RCT rct; xvt_vobj_get_client_rect(win(), &rct); const TSize size = _book->page_size(); const PNT pag = log2dev(size.x, size.y); const PNT ori = log2dev(0, 0); if (ori.h > 0) rct.left = ori.h; if (ori.v > 0) rct.top = ori.v; if (pag.h < rct.right) rct.right = pag.h; if (pag.v < rct.bottom) rct.bottom = pag.v; hide_pen(); // Disegna foglio bianco set_brush(COLOR_WHITE); xvt_dwin_draw_rect(win(), &rct); set_pen(COLOR_GRAY); // Disegna ombra grigia const PNT poly[4] = { {rct.bottom,rct.left+1}, {rct.bottom,rct.right+1}, {rct.top+1,rct.right+1}, {0,0} }; xvt_dwin_draw_polyline(win(), poly, 3); // Disegna ombra if (_grid) { const TSize res = _book->page_res(); const int lpi = _book->lpi(); for (int i = 1; lpi > 0; i++) { set_pen(i%lpi ? XVT_MAKE_COLOR(232,232,255) : XVT_MAKE_COLOR(255,192,255)); const short y = short(i * res.y / lpi); if (y > size.y) break; line(0, y, (short)size.x, y); } const int cpi = _book->cpi(); for (int j = 1; cpi > 0; j++) { set_pen(j%10 ? XVT_MAKE_COLOR(232,232,255) : XVT_MAKE_COLOR(255,192,255)); const short x = short(j * res.x / cpi); if (x > size.x) break; line(x, 0, x, (short)size.y); } } _book->print_page(*this, pg); } void TPrint_preview_window::update() { clear(MASK_BACK_COLOR); draw_page(_page); size_t last_page = _page; const size_t pages = _book->pages(); if (_page < pages) { const TPoint o = origin(); set_origin(0, 0); const TSize size = _book->page_size(); const PNT pag = log2dev(size.x, size.y); RCT rct; xvt_vobj_get_client_rect(win(), &rct); const int nh = rct.right / (pag.h+2), nv = rct.bottom / (pag.v+2); if (nh > 1 || nv > 1) { for (int v = 0; v < nv; v++) { for (int h = 0; h < nh; h++) { if ((h > 0 || v > 0) && last_page < pages) { set_origin(o.x - h*rct.right/nh, o.y - v*rct.bottom/nv); draw_page(++last_page); } } } } set_origin(o.x, o.y); } TString80 str; if (last_page > _page) str.format(FR("Pagine da %u a %u di %u"), _page, last_page, pages); else str.format(FR("Pagina %u di %u"), _page, pages); statbar_set_title(TASK_WIN, str); TMask& m = owner().mask(); m.enable(DLG_FIRSTREC, _page > 1); m.enable(DLG_PREVREC, _page > 1); m.enable(DLG_FINDREC, pages > 1); m.enable(DLG_NEXTREC, _page < pages); m.enable(DLG_LASTREC, _page < pages); } #define POPUP_FIRST 20883 #define POPUP_PREV 20884 #define POPUP_NEXT 20885 #define POPUP_SEARCH 20886 #define POPUP_LAST 20887 #define POPUP_ZOOMIN 20888 #define POPUP_ZOOMOUT 20889 #define POPUP_GRID 20890 void TPrint_preview_window::popup_menu(EVENT* ep) { MENU_ITEM menu[16]; // Stiamo larghi memset(menu, 0, sizeof(menu)); menu[0].tag = POPUP_FIRST; menu[0].text = (char*)TR("Prima"); menu[0].enabled = _page > 1; menu[1].tag = POPUP_PREV; menu[1].text = (char*)TR("Indietro"); menu[1].enabled = _page > 1; menu[2].tag = POPUP_NEXT; menu[2].text = (char*)TR("Avanti"); menu[2].enabled = _page < _book->pages(); menu[3].tag = POPUP_LAST; menu[3].text = (char*)TR("Ultima"); menu[3].enabled = _page < _book->pages(); menu[4].tag = -1; menu[4].separator = true; menu[5].tag = POPUP_ZOOMIN; menu[5].text = (char*)TR("Zoom +"); menu[5].enabled = _zoom < SCREENDPI*4; menu[6].tag = POPUP_ZOOMOUT; menu[6].text = (char*)TR("Zoom -"); menu[6].enabled = _zoom > SCREENDPI/4; menu[7].tag = -1; menu[7].separator = true; menu[8].tag = POPUP_GRID; menu[8].text = (char*)TR("Griglia"); menu[8].enabled = true; menu[8].checkable = true; menu[8].checked = _grid; const PNT& p = ep->v.mouse.where; xvt_menu_popup(menu, win(), p, XVT_POPUP_CENTER, 0); } void TPrint_preview_window::page_select() { TMask m(TR("Ricerca"), 1, 28, 4); m.add_number(101, 0, PR("Pagina "), 1, 1, 4, "U").check_type(CHECK_REQUIRED); m.add_button(DLG_OK, 0, "", -12, -1, 10, 2); m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2); if (m.run()) { _page = m.get_int(101); if (_page < 1) _page = 1; if (_page > _book->pages()) _page = _book->pages(); } } const TReport_link* TPrint_preview_window::find_link(const PNT& pnt) const { int primo = 0, ultimo = _plinks.last(); while (primo <= ultimo) { const int in_mezzo = (primo+ultimo)/2; const TReport_link* lnk = (const TReport_link*)_plinks.objptr(in_mezzo); const int cmp = lnk->hit_test(pnt); if (cmp == 0) return lnk; if (cmp < 0) ultimo = in_mezzo-1; else primo = in_mezzo+1; } return NULL; } long TPrint_preview_window::handler(WINDOW win, EVENT* ep) { static PNT ptPan; switch (ep->type) { case E_MOUSE_MOVE: switch (ep->v.mouse.button) { case 2: { const PNT& ptCur = ep->v.mouse.where; TPoint orig = origin(); orig.x += ptPan.h - ptCur.h; orig.y += ptPan.v - ptCur.v; update_thumb(orig.x, orig.y); force_update(); ptPan = ptCur; } break; default: if (find_link(ep->v.mouse.where) != NULL) xvt_win_set_cursor(win, 8004); // Ditino else xvt_win_set_cursor(win, CURSOR_ARROW); // Freccia break; } break; case E_MOUSE_DOWN: switch (ep->v.mouse.button) { case 0: { const TReport_link* lnk = find_link(ep->v.mouse.where); if (lnk != NULL && _book != NULL) _book->on_link(*lnk); } break; case 1: popup_menu(ep); break; case 2: ptPan = ep->v.mouse.where; break; default: break; } break; case E_COMMAND: { bool processed = true; switch(ep->v.cmd.tag) { case POPUP_FIRST : processed = _page > 1; if (processed) _page = 1; break; case POPUP_PREV : processed = _page > 1; if (processed) _page--; break; case POPUP_SEARCH : page_select(); break; case POPUP_NEXT : processed = _page < _book->pages(); if (processed) _page++; break; case POPUP_LAST : processed = _page < _book->pages(); if (processed) _page = _book->pages(); break; case POPUP_ZOOMIN : if (_zoom < SCREENDPI*4) { _zoom += SCREENDPI/8; update_scroll_range(); } break; case POPUP_ZOOMOUT: if (_zoom > SCREENDPI/4) { _zoom -= SCREENDPI/8; update_scroll_range(); } break; case POPUP_GRID : _grid = !_grid; break; default:processed = false; break; } if (processed) force_update(); } break; case E_HSCROLL: case E_VSCROLL: { int kx = 0, ky = 0; int& k = ep->type == E_HSCROLL ? kx : ky; switch(ep->v.scroll.what) { case SC_PAGE_UP : k = -3; break; case SC_LINE_UP : k = -1; break; case SC_PAGE_DOWN: k = +3; break; case SC_LINE_DOWN: k = +1; break; default : TField_window::handler(win, ep); break; } if (k != 0) do_scroll(kx, ky); break; } break; default: return TField_window::handler(win, ep); } return 0L; } void TPrint_preview_window::do_scroll(int kx, int ky) { const TPoint& r = range(); TPoint orig = origin(); orig.x += kx * r.x/8; orig.y += ky * r.y/6; if (orig.x < 0) orig.x = 0; if (orig.x > r.x) orig.x = r.x; if (orig.y < 0) orig.y = 0; if (orig.y > r.y) orig.y = r.y; if (orig != origin()) { update_thumb(orig.x, orig.y); force_update(); } } bool TPrint_preview_window::on_key(KEY k) { bool ok = true; switch (k) { case '+' : dispatch_e_menu(win(), POPUP_ZOOMIN); break; case '-' : dispatch_e_menu(win(), POPUP_ZOOMOUT); break; case K_HOME : case K_LHOME : dispatch_e_menu(win(), POPUP_FIRST); break; case K_PREV : dispatch_e_menu(win(), POPUP_PREV); break; case K_NEXT : dispatch_e_menu(win(), POPUP_NEXT); break; case K_END: case K_LEND : dispatch_e_menu(win(), POPUP_LAST); break; case K_LEFT : do_scroll(-1, 0); break; case K_RIGHT : do_scroll(+1, 0); break; case K_UP : do_scroll(0, -1); break; case K_DOWN : do_scroll(0, +1); break; case 'G' : case 'g' : dispatch_e_menu(win(), POPUP_GRID); break; default : if (k > K_CTRL) dispatch_e_char(parent(), k); // Gestione acceleratori else ok = TField_window::on_key(k); break; }; return ok; } TPrint_preview_window::TPrint_preview_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner, TBook* book) : TField_window(x, y, dx, dy, parent, owner), _book(book), _page(1), _zoom(SCREENDPI), _grid(false) { _pixmap = true; _grid = ini_get_bool(CONFIG_GUI, "Preview", "Grid"); } TPrint_preview_window::~TPrint_preview_window() { ini_set_string(CONFIG_GUI, "Preview", "Grid", _grid ? "X" : ""); } /////////////////////////////////////////////////////////// // TPrint_preview_field /////////////////////////////////////////////////////////// class TPrint_preview_field : public TWindowed_field { TBook* _book; protected: virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent); public: TPrint_preview_field(TMask* m, TBook* book) : TWindowed_field(m), _book(book) { } }; TField_window* TPrint_preview_field::create_window(int x, int y, int dx, int dy, WINDOW parent) { return new TPrint_preview_window(x, y, dx, dy, parent, this, _book); } /////////////////////////////////////////////////////////// // TPreview_mask /////////////////////////////////////////////////////////// class TPreview_mask : public TAutomask { TBook* _book; TPrint_preview_field* _pvf; protected: virtual bool on_key(KEY k); virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); virtual TMask_field* parse_field(TScanner& scanner); virtual long handler(WINDOW win, EVENT* ep); public: TPreview_mask(TBook* book); }; bool TPreview_mask::on_key(KEY k) { switch (k) { case '+' : case '-' : case K_HOME : case K_LHOME: case K_LEFT : case K_RIGHT: case K_END : case K_LEND : case K_PREV : case K_NEXT : case 'G' : case 'g' : if (_pvf != NULL) return _pvf->win().on_key(k); break; default: break; } return TAutomask::on_key(k); } long TPreview_mask::handler(WINDOW win, EVENT* ep) { // Riflessione eventi di scroll switch (ep->type) { case E_HSCROLL: case E_VSCROLL: if (_pvf != NULL) ::dispatch_event(_pvf->win().win(), *ep, false); break; case E_SIZE: if (_pvf != NULL) { TPrint_preview_window& ppw = (TPrint_preview_window&)_pvf->win(); xvt_vobj_maximize(ppw.win()); ppw.update_scroll_range(); } break; default: return TAutomask::handler(win, ep); } return 0L; } bool TPreview_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { if (e == fe_button) { short idm = 0; switch (o.dlg()) { case DLG_FIRSTREC: idm = POPUP_FIRST; break; case DLG_PREVREC : idm = POPUP_PREV; break; case DLG_FINDREC : idm = POPUP_SEARCH; break; case DLG_NEXTREC : idm = POPUP_NEXT; break; case DLG_LASTREC : idm = POPUP_LAST; break; default: break; } if (idm > 0 && _pvf != NULL) dispatch_e_menu(_pvf->win().win(), idm); } return true; } TMask_field* TPreview_mask::parse_field(TScanner& scanner) { if (scanner.token().starts_with("PR")) { _pvf = new TPrint_preview_field(this, _book); return _pvf; } return TAutomask::parse_field(scanner); } TPreview_mask::TPreview_mask(TBook* book) : _book(book), _pvf(NULL) { read_mask("bagn008", 0, -1); set_handlers(); } /////////////////////////////////////////////////////////// // TWindow_printer /////////////////////////////////////////////////////////// class TWindow_printer : public TWindow { long _pvr, _phr; long _ph, _pw; protected: bool is_ok() const { return win() != NULL_WIN; } virtual PNT log2dev(long x, long y) const; public: bool print_doc(const TFilename& name); TWindow_printer(PRINT_RCD* rcd, const char* title); ~TWindow_printer(); }; // Converte da coordinate espresse in 1/720 di pollice a coordinate fisiche su carta PNT TWindow_printer::log2dev(long x, long y) const { PNT ptdev; ptdev.h = short((x * _phr) / BOOKDPI); ptdev.v = short((y * _pvr) / BOOKDPI); #ifdef DBG if(ptdev.h < 0 || ptdev.h >= _pw) xvtil_statbar_set("Coordinata X errata"); if(ptdev.v < 0 || ptdev.v >= _ph) xvtil_statbar_set("Coordinata Y errata"); #endif return ptdev; } bool TWindow_printer::print_doc(const TFilename& name) { bool ok = name.exist(); if (ok) { const bool printing = is_ok(); if (printing) xvt_print_suspend_thread(); ok = xvt_sys_goto_url(name, "print") != 0; if (printing) xvt_print_restart_thread(); } return ok; } TWindow_printer::TWindow_printer(PRINT_RCD* rcd, const char* title) { WINDOW prwin = xvt_print_create_win(rcd, title); set_win(prwin); _pixmap = true; xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, rcd, &_ph, &_pw, &_pvr, &_phr); } TWindow_printer::~TWindow_printer() { if (is_ok()) { xvt_vobj_destroy(win()); set_win(NULL_WIN); } } /////////////////////////////////////////////////////////// // Writing a page /////////////////////////////////////////////////////////// // Converte da coordinate logiche (1/100 caratteri) a coordinate di stampa TPoint TBook::log2dev(const TReport_pnt& ptlog) const { TPoint ptdev; ptdev.x = (ptlog.x * BOOKDPI) / (100 * cpi()); ptdev.y = (ptlog.y * BOOKDPI) / (100 * lpi()); return ptdev; } TRectangle TBook::log2dev(const TReport_rct& rctlog) const { const TReport_pnt& lp0 = rctlog; TReport_pnt lp1 = lp0; lp1 += rctlog.size(); const TPoint p0 = log2dev(lp0); const TPoint p1 = log2dev(lp1); return TRectangle(p0, p1-p0); } PNT TBook::log2pix(const TReport_pnt& ptlog) const { const TPoint ptdev = log2dev(ptlog); return _printwin->log2dev(ptdev.x, ptdev.y); } // Converte da coordinate di stampa (1/720 di pollice) in pixel short TBook::book2pix(int t) const { const PNT pnt = _printwin->log2dev(t, 0); return pnt.h; } int TBook::compute_text_frame(const TString& tmp, const TReport_font& font, TReport_rct& rect, TString_array& para) const { // Calcola la larghezza massima e l'altezza di 10 righe // const TReport_rct rect_riga(0,0,rect.width(),1000); // const PNT size = log2pix(rect_riga.size()); // const int max_row_width = size.h; // const int def_10row_height = size.v; const PNT p0 = log2pix(TReport_pnt(0,0)); const PNT p1 = log2pix(TReport_pnt(rect.width(),1000)); const int max_row_width = p1.h-p0.h; const int def_10row_height = p1.v-p0.v; if (max_row_width < 100) return 0; // Con un rettangolo quasi vuoto, si combina poco di buono! WINDOW w = _printwin->win(); CHECK(w == PRINTER_WIN, "Finestra di stampa non valida"); xvt_dwin_set_font(w, font.get_xvt_font(*_printwin)); TToken_string p(tmp, '\n'); FOR_EACH_TOKEN(p, line) para.add(line); for (int i = 0; i < para.items(); i++) { TString& row = para.row(i); int pix = xvt_dwin_get_text_width(w, row, -1); if (pix > max_row_width) { int good_len = 0; const int first_space = row.find(' '); if (first_space >= 0) { // Linea con almeno uno spazio for (int i = first_space; row[i]; i++) if (row[i] == ' ') { pix = xvt_dwin_get_text_width(w, row, i); if (pix <= max_row_width) good_len = i; else break; } } if (good_len == 0) // Puo' succedere per linee senza spazi o con parole lunghissime { for (good_len = row.len() * max_row_width / w + 1; good_len > 0; good_len--) { const int pix = xvt_dwin_get_text_width(w, row, good_len); if (pix < max_row_width) break; } } TString next_row = row.mid(good_len); next_row.ltrim(); para.insert(next_row, i+1); row.cut(good_len); } } int leading, ascent, descent; xvt_dwin_get_font_metrics(w, &leading, &ascent, &descent); int ky10 = (leading + ascent + descent) * 10; // Aggiusta l'altezza di 10 righe standard, se necessario if (ky10 < def_10row_height && ky10 > 75*def_10row_height/100) ky10 = def_10row_height; int h = para.items() * ky10 * 100 / def_10row_height; const int resto = h % 100; if (resto != 0) h += 100-resto; rect.set_height(h); return ky10; } TSize TBook::page_size() const { if (_page_size.x <= 0 || _page_size.y <= 0) // Valori nulli: mi invento un A4 { TSize pag = page_res(); pag.x *= 8; pag.y *= 11; return pag; } return _page_size; } TSize TBook::page_res() const { return TSize(BOOKDPI, BOOKDPI); } bool TBook::open_page() { if (_out == NULL) // Sto per scrivere la prima pagina { _out = new ofstream(_file); _max_frame.x = _max_frame.y = 0; } _page++; _index.add_long((long)_out->tellp(), page()); // Scrive alla posizione 1 l'inizio di pagina 1 *_out << "" << endl; } } void TBook::define_frame(const TRectangle& rect) { if (rect != _rect) { _rect = rect; *_out << "" << endl; // Ignora campi malamente copia/incollati a coordinate negative! if (_rect.x >= 0 && _rect.y >= 0) { if (_rect.right() > _max_frame.x) _max_frame.x = _rect.right(); if (_rect.bottom() > _max_frame.y) _max_frame.y = _rect.bottom(); } } } void TBook::define_frame(const TReport_rct& rr) { const TRectangle rb = log2dev(rr); // Converto rr da coordinate logiche a rb a 720 DPI define_frame(rb); // Salvo le coordinate a 720 DPI } void TBook::set_clip(long top, long bottom) { if (bottom >= top) { const TReport_rct rect(0, top, logical_page_width(), bottom-top); define_frame(rect); *_out << "" << endl; } else *_out << "" << endl; } void TBook::draw_text(const TReport_rct& r, const char* txt, const char* section) { if (txt && *txt) { const TFixed_string str(txt); if (str.full()) { define_frame(r); *_out << "" << endl << str << endl << "" << endl; } } } void TBook::draw_text(const TReport_rct& r, const TString_array& txt, const char* section) { if (!txt.empty()) { define_frame(r); *_out << "" << endl; for (int i = 0; i < txt.items(); i++) { const char* line = txt.row(i); *_out << line << endl; } *_out << "" << endl; } } void TBook::draw_book_pages(const TReport_rct& r) { define_frame(r); *_out << "" << endl; } void TBook::set_pen(COLOR color, int width, PEN_STYLE style) { if (_tools.pen.color != color || _tools.pen.width != width || _tools.pen.style != style) { _tools.pen.color = color; _tools.pen.width = width; _tools.pen.style = style; *_out << "" << endl; } } void TBook::set_brush(COLOR color, PAT_STYLE pattern, int shade_angle) { if (_tools.brush.color != color || _tools.brush.pat != pattern || shade_angle != _shade_angle) { _tools.brush.color = color; _tools.brush.pat = pattern; _shade_angle = shade_angle; *_out << "" << endl; } } void TBook::set_text_color(COLOR fore, COLOR back, bool opaque) { if (_tools.fore_color != fore || _tools.back_color != back || _tools.opaque_text != short(opaque)) { _tools.fore_color = fore; _tools.back_color = back; _tools.opaque_text = opaque; *_out << "" << endl; } } void TBook::set_text_align(char halign, char valign) { if (_horizontal_alignment != halign || _vertical_alignment != valign) { _horizontal_alignment = halign; _vertical_alignment = valign; *_out << "" << endl; } } void TBook::draw_rectangle(const TReport_rct& r) { define_frame(r); *_out << "" << endl; } void TBook::draw_round_rectangle(const TReport_rct& r, int radius) { define_frame(r); *_out << "" << endl; } void TBook::draw_ellipse(const TReport_rct& r) { define_frame(r); *_out << "" << endl; } void TBook::draw_line(const TReport_rct& r) { define_frame(r); *_out << "" << endl; } void TBook::draw_link(const TReport_rct& rect, const char* text, const char* link) { define_frame(rect); *_out << "" << text << "" << endl; } void TBook::draw_image(const TReport_rct& rect, const char* name) { define_frame(rect); *_out << "" << endl; } void TBook::add_doc(const TString& name) { if (name.full()) { TBook::open_page(); *_out << ""<< endl; TBook::close_page(); } } /////////////////////////////////////////////////////////// // Reading a page /////////////////////////////////////////////////////////// bool get_xml_string(const TString& line, const char* attr, TString& value) { TString80 str; str << ' ' << attr << '='; const int pos = line.find(str); if (pos >= 0) { const int apicia = pos + strlen(attr) + 2; if (line[apicia] == '"') { const int apicic = line.find('"', apicia+1); if (apicic > apicia) { value = line.sub(apicia+1, apicic); return true; } } else { const int space = line.find(' ', apicia); if (space > 0) { value = line.sub(apicia, space); return true; } } } return false; } int get_xml_int(const TString& line, const char* attr, int def) { TString16 str; if (get_xml_string(line, attr, str)) def = atoi(str); return def; } void TBook::print_doc(TWindow& win, const TFilename& name) { const bool print = win.win() == PRINTER_WIN; if (print) { TWindow_printer& wp = (TWindow_printer&)win; wp.print_doc(name); } else { const TSize ps = page_size(); TRectangle rect(TPoint(0,0), ps); RCT page; win.log2dev(rect, page); advanced_draw_text_line(win.win(), name, page, 'C', 'T'); TString8 ext = name.ext(); ext.lower(); if (ext == "bmp" || ext == "gif" || ext == "jpg" || ext == "jpeg" || ext == "png") { const TImage* img = _images.image(name); if (img != NULL) { const TSize pr = page_res(); rect.deflate(pr.x/2, pr.y/2); win.log2dev(rect, page); const double sx = double(page.right) / double(img->width()); const double sy = double(page.bottom) / double(img->height()); const double s = sx < sy ? sx : sy; const int w = int(s * img->width()); const int h = int(s * img->height()); RCT rct; rct.left = (page.right+page.left-w)/2; rct.right = rct.left + w; rct.top = (page.bottom+page.top-h)/2; rct.bottom = rct.top + h; img->draw(win.win(), rct); } } } } bool TBook::print_page(TWindow& win, size_t page) { if (page <= 0 || page > pages()) return false; WINDOW w = win.win(); const bool preview = w != PRINTER_WIN; int shade_angle = 0; RCT visible; if (preview) { xvt_vobj_get_client_rect(w, &visible); TPrint_preview_window& pw = (TPrint_preview_window&)win; pw.alinks().destroy(); } else xvt_rect_set_null(&visible); TToken_string str(1024, '='); TString_array paragrafo; // Testo completo di un campo // Calcolo altezza riga standard const TRectangle rect_riga(0,0,BOOKDPI*10/cpi(),BOOKDPI*10/lpi()); RCT rct_riga; win.log2dev(rect_riga, rct_riga); const int default_10row_height = rct_riga.bottom - rct_riga.top; const streampos pos = _index.get_long(page); ifstream ifs(_file); ifs.seekg(pos); _rect.set(-1,-1,0,0); // Reset logical frame (1/720 coords) RCT rct = { 0,0,0,0 }; // Physical frame (pixel coords) _horizontal_alignment = 'L'; // Reset text alignment _vertical_alignment = 'T'; bool page_is_open = false; // Stato attuale della pagina della stampante while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str.starts_with("= visible.top && rct.top <= visible.bottom) { TString link; if (get_xml_string(str, "href", link)) { TPrint_preview_window& pw = (TPrint_preview_window&)win; TAssoc_array& links = pw.alinks(); TToken_string tok(link, '.'); TString80 table, field, key; tok.get(0, table); tok.get(1, field); key << table << ',' << _rect.y; TReport_link* rl = (TReport_link*)links.objptr(key); if (rl == NULL) { rl = new TReport_link(table); links.add(key, rl); } const int inizio = str.find('>')+1; const int fine = str.rfind('<'); const TString& stringona = str.sub(inizio, fine); rl->set(field, stringona); rl->add_rect(rct); if (stringona.full() && rct.right > rct.left) // Possono esserci campi chiave nascosti { DRAW_CTOOLS dct; xvt_dwin_get_draw_ctools(w, &dct); XVT_FNTID oldfont = xvt_dwin_get_font(w); XVT_FNTID newfont = xvt_font_create(); xvt_font_copy(newfont, oldfont, XVT_FA_ALL); xvt_font_set_style(newfont, xvt_font_get_style(oldfont) | XVT_FS_UNDERLINE); xvt_dwin_set_font(w, newfont); win.set_color(COLOR_BLUE, dct.back_color); advanced_draw_text_line(w, stringona, rct, _horizontal_alignment, _vertical_alignment); win.set_color(dct.fore_color, dct.back_color); xvt_dwin_set_font(w, oldfont); xvt_font_destroy(oldfont); xvt_font_destroy(newfont); } } } continue; } if (str.starts_with(" const COLOR col = str.get_long(1); const PAT_STYLE pat = (PAT_STYLE)str.get_long(); if (pat <= PAT_HOLLOW) win.hide_brush(); else win.set_brush(col, pat); shade_angle = (pat == PAT_SPECIAL) ? str.get_int() : 0; continue; } if (str.starts_with("") { xvt_dwin_draw_oval(w, &rct); continue; } if (str.starts_with(" const long x = str.get_long(1); const long y = str.get_long(); const long dx= str.get_long(); const long dy= str.get_long(); _rect.set(x, y, dx, dy); // Rettangolo in 1/720 di pollice win.log2dev(_rect, rct); // Rettangolo in pixel su carta continue; } if (str.starts_with("draw(w, rct); } continue; } if (str == "") { const PNT fr = { rct.top, rct.left }; const PNT to = { rct.bottom, rct.right }; xvt_dwin_draw_set_pos(w, fr); xvt_dwin_draw_line(w, to); continue; } if (str == "") { TString8 str; str.format("%u", pages()); advanced_draw_text_line(w, str, rct, _horizontal_alignment, _vertical_alignment); continue; } if (str.starts_with(" COLOR col = str.get_long(1); int width = str.get_int(); PEN_STYLE style = (PEN_STYLE)str.get_int(); if (width < 0) win.hide_pen(); else { double t = 0; if (preview) { const TPrint_preview_window& pw = (const TPrint_preview_window&)win; t = pw.book2pix(width ? 100*width : 50)/10.0; if (t < 1.0) // Sfuma le linee sottili in anteprima col = blend_colors(col, COLOR_WHITE, t); } else { t = book2pix(width ? 10*width : 5); // width e' in 1/72 di pollice } const int thickness = int(t+0.5); // Arrotonda all'unita' piu' vicina win.set_pen(col, thickness); } continue; } if (str == "") { DRAW_CTOOLS ct; xvt_dwin_get_draw_ctools(w, &ct); if (ct.brush.pat == PAT_SPECIAL) { if (shade_angle >= 0) xvt_dwin_draw_gradient_linear(w, &rct, ct.back_color, ct.brush.color, shade_angle); else xvt_dwin_draw_gradient_circular(w, &rct, ct.back_color, ct.brush.color, NULL); if (ct.pen.style != PAT_HOLLOW) { win.hide_brush(); xvt_dwin_draw_rect(w, &rct); } } else xvt_dwin_draw_rect(w, &rct); continue; } if (str.starts_with(" { paragrafo.destroy(); while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str == "") break; paragrafo.add(str); } advanced_draw_paragraph(win.win(), paragrafo, rct, _horizontal_alignment, _vertical_alignment, default_10row_height); continue; } if (str.starts_with(" _horizontal_alignment = str.get_char(1); _vertical_alignment = str.get_char(); continue; } if (str.starts_with(" const COLOR fore = str.get_long(1); const COLOR back = str.get_long(); const int opaque = str.get_int(); win.set_color(fore, back); win.set_opaque_text(opaque != 0); continue; } } if (preview) { TPrint_preview_window& pw = (TPrint_preview_window&)win; TAssoc_array& alinks = pw.alinks(); TPointer_array& plinks = pw.plinks(); plinks.destroy(); if (alinks.items() > 0) { FOR_EACH_ASSOC_OBJECT(alinks, h, key, l) plinks.add((TReport_link*)l); plinks.sort(); } } else { if (page_is_open) xvt_print_close_page(_rcd); } return true; } bool TBook::export_text(TFilename& fname, bool signature) { TString ext = fname.ext(); ext.lower(); if (ext == "pdf") return export_pdf(fname, signature); if (ext.starts_with("xls") || ext.starts_with("htm")) return export_excel(fname, signature); TToken_string str(1024, '='); ifstream ifs(_file); ofstream ofs(fname); TString_array page; int row, col, wid, hei; const TSize res = page_res(); while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str.starts_with(" const long x = str.get_long(1); const long y = str.get_long(); const long dx= str.get_long(); const long dy= str.get_long(); row = y * lpi() / res.y; col = x * cpi() / res.x; wid = dx * cpi() / res.x; hei = dy * lpi() / res.y; } else if (str == "") { if (hei == 0 && wid > 0) { if (page.objptr(row) == NULL) page.add("", row); TString& line = page.row(row); for (int i = 0; i < wid; i++) { const int j = i+col; if (j >= line.len()) line.overwrite("-", j); else { if (line[j] == '|') line[j] = '+'; else { if (line[j] == ' ') line[j] = '-'; } } } } else if (hei > 0 && wid == 0) { for (int i = row; i < row+hei; i++) { if (page.objptr(i) == NULL) page.add("", i); TString& line = page.row(i); if (line.len() <= col) line.overwrite("|", col); else { if (line[col] == '-') line[col] = '+'; else { if (line[col] == ' ') line[col] = '|'; } } } } } if (str.starts_with("") { TString8 str; str.format("%u", pages()); TString& line = page.row(row); switch (_horizontal_alignment) { case 'C': line.overwrite(str, col+(wid-str.len())/2); break; case 'R': line.overwrite(str, col+wid-str.len()); break; default : line.overwrite(str, col); break; } } else if (str.starts_with(" { TString stringona; while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str == "") break; stringona << str << '\n'; } stringona.rtrim(); TParagraph_string para(stringona, wid); for (int i = 0; i < hei && i < para.items(); i++) { const int j = row+i; if (page.objptr(j) == NULL) page.add("", j); str = para.get(); TString& line = page.row(j); switch (_horizontal_alignment) { case 'C': line.overwrite(str, col+(wid-str.len())/2); break; case 'R': line.overwrite(str, col+wid-str.len()); break; default : line.overwrite(str, col); break; } } } else if (str.starts_with(" _horizontal_alignment = str.get_char(1); _vertical_alignment = str.get_char(); } } ofs.close(); if (signature && main_app().has_module(FDAUT)) { char outfile[_MAX_PATH] = ""; if (xvt_sign_file(fname, outfile)) fname = outfile; } return true; } static void reformat_excel(TString& str) { str.trim(); if (str.not_empty()) { bool is_number = true; bool is_mig = false; bool is_dec = false; for (int i = 0; str[i]; i++) { if (strchr("0123456789.,", str[i]) != NULL) { if (str[i] == '.') is_mig = true; if (str[i] == ',') is_dec = true; } else { is_number = false; break; } } if (is_number && (is_dec || is_mig)) { if (is_mig) str.strip("."); if (is_dec) str.replace(',', '.'); const real n = str; str = n.stringe(); } } } bool TBook::export_excel(TFilename& fname, bool signature) { TTabulator tab; int row = 0, col = 0, wid = 0; TToken_string str(1024, '='); const TSize res = page_res(); if (tab.empty()) // dummy test { ifstream ifs(_file); while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str.starts_with(" const long x = str.get_long(1); const long y = str.get_long(); const long w = str.get_long(); row = y * lpi() / res.y; col = x * cpi() / res.x; wid = w * cpi() / res.x; } else if (str.starts_with("" && !ifs.eof()) ifs.getline(str.get_buffer(), str.size()); } else if (str.starts_with(" '0'; if (!do_export) { switch (section[0]) { case 'H': do_export = (pageno == 1); break; // Stampa gli header solo sulla prima pagina default : break; } } while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str == "") break; if (do_export && str.full()) { const int pos = tab.find_column(col, wid); if (pos >= 0) { TToken_string* line = (TToken_string*)page.objptr(row); if (line == NULL) { line = new TToken_string(256,'\t'); page.add(line, row); } reformat_excel(str); line->add(str, pos); } } } } else if (str.starts_with("full()) { ofs << *line << endl; // Stampo e... line->cut(0); // ... svuoto } } } } if (signature && main_app().has_module(FDAUT)) { char outfile[_MAX_PATH] = ""; if (xvt_sign_file(fname, outfile)) fname = outfile; } return true; } bool TBook::init() { if (is_pdf()) { int size; _rcd = xvt_print_create_by_name(&size, XVT_PDF_PRINTER_NAME); } else _rcd = printer().get_printrcd(); if (!xvt_print_is_valid(_rcd)) return error_box(TR("Stampante non valida")); long ph, pw, pvr, phr; xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, _rcd, &ph, &pw, &pvr, &phr); if (pw <= 0 || ph <= 0) return error_box(TR("Dimensioni pagina NULLE")); if (pvr <= 0 || phr <= 0) return error_box(TR("Risoluzione stampante NULLA")); if (_page_size.x > 0 && (_page_size.x > _page_size.y) != (pw > ph)) // Orientamento incongruente { xvt_app_escape (XVT_ESC_SET_PRINTER_INFO, _rcd, &pw, &ph, NULL, NULL); // Scambio dimensioni xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, _rcd, &ph, &pw, &pvr, &phr); // Rileggo dimensioni e risoluzione } _page_size.y = ph * BOOKDPI / pvr; _page_size.x = pw * BOOKDPI / phr; if (_printwin != NULL) delete _printwin; _printwin = new TWindow_printer(_rcd, _pdf_file); return _printwin->win() != NULL_WIN; } void TBook::split_file(int colonne) { TProgind pi(pages(), TR("Rigenerazione pagine"), true, true); TFilename temp; temp.temp(); ofstream out(temp); TPointer_array index; close_output(); // Chiudo file di stampa eventualmente aperto ifstream ifs(_file); // Apro file di stampa da splittare TToken_string str(1024, '='); for (unsigned int page = 1; page <= pages(); page++) { if (!pi.setstatus(page)) break; const streampos pos = _index.get_long(page); for (int c = 0; c < colonne; c++) { const TRectangle rct_page(c*_page_size.x, 0, _page_size.x, _page_size.y); ifs.seekg(pos); while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str.starts_with("", p); } else if (str.starts_with(" const long x = str.get_long(1); const long y = str.get_long(); const long dx= str.get_long(); const long dy= str.get_long(); str.format("", x-rct_page.x, y, dx, dy); } else if (str.starts_with("", index.last()); } out << str << endl; if (str.starts_with("close(); delete _out; } _out = NULL; } void TBook::join_file(int pps) { TProgind pi(pages(), TR("Rigenerazione pagine"), true, true); TFilename temp; temp.temp(); ofstream out(temp); close_output(); // Chiudo file di stampa eventualmente aperto ifstream ifs(_file); // Apro file di stampa da splittare _index.destroy(); // Distruggo il vecchio indice delle pagine che poi ricostruisco _pages = (_pages - 1) / pps + 1; // Ricalcolo in nuovo numero di pagine totali int in_page = 1; int out_page = 0; int off_page = 0; TToken_string str(1024, '='); while (!ifs.eof()) { ifs.getline(str.get_buffer(), str.size()); if (str.starts_with(" in_page = str.get_int(1); const int sub = ((in_page - 1) % pps); // off_page = sub * logical_page_height() / pps; off_page = sub * page_size().y / pps; if (sub == 0) { out_page = (in_page - 1) / pps + 1; _index.add_long((long)out.tellp(), out_page); str.format("", out_page); } else { // E' utile resettare l'allineamento dei testi :-) str = ""; } } else if (str.starts_with(" 0) { // const long x = str.get_long(1); const long y = str.get_long(); const long dx= str.get_long(); const long dy= str.get_long(); str.format("", x, y+off_page, dx, dy); } else if (str.starts_with("", out_page); in_page = 0; } else continue; } out << str << endl; } if (in_page != 0) // Non ho ancora scritto l'ultimo fine pagina { str.format("", out_page); out << str << endl; } out.close(); // Sostituisce il file di stampa con quello joinato fcopy(temp, _file); xvt_fsys_remove_file(temp); } bool TBook::can_split(int pages) const { return yesno_box(FR("Si desidera suddividere le pagine su %d fogli"), pages); } bool TBook::can_merge(int pages) const { return yesno_box(FR("Si desidera raggruppare %d pagine per foglio"), pages); } bool TBook::split_file_if_needed() { if (_max_frame.x > 100 && _max_frame.y > 100) // Evita calcolo splitting se possibile { const TSize sheet = page_size(); const int spp = _max_frame.x > sheet.x ? (_max_frame.x+sheet.x-1) / sheet.x : 1; if (spp > 1 && can_split(spp)) // Sheets per page { split_file(spp); } else { const int pps = (_max_frame.y > 100 && pages() > 1) ? sheet.y / _max_frame.y : 1; if (pps > 1 && can_merge(pps)) join_file(pps); } _max_frame.x = _max_frame.y = 0; // Impedisce ulteriore splitting } return true; } bool TBook::main_loop() { _print_aborted = !init(); if (!_print_aborted) { if (_pageto < _pagefrom) _pageto = pages(); for (word copy = 0; copy < _copies && !_print_aborted; copy++) { for (size_t page = _pagefrom; page <= _pageto && !_print_aborted; page++) _print_aborted = !print_page(*_printwin, page); } } if (is_pdf()) { xvt_print_destroy(_rcd); _rcd = NULL; _pdf_file.cut(0); } return !_print_aborted; } static BOOLEAN main_loop_callback(long jolly) { TBook* pp = (TBook*)jolly; return pp->main_loop(); } bool TBook::print(size_t pagefrom, size_t pageto, word copies) { if (pages() <= 0) return false; TPrinter& p = printer(); if (pagefrom == 0) { TMask msk("bagn003"); msk.set(F_PRINTER, p.printername()); msk.set(F_FORM, p.get_form_name()); msk.set(F_FONT, p.fontname()); msk.set(F_SIZE, p.get_char_size()); msk.set(F_LINES, p.get_lines_per_inch()); msk.set(F_ORIENT, p.is_portrait() ? 1 : 2); msk.set(F_ISGRAPHICS, p.isgraphics()); msk.set(F_FROMPAGE, 1); msk.set(F_TOPAGE, pages()); msk.set(F_COPIES, 1); if (msk.run() == K_ENTER) { _copies = max(msk.get_int(F_COPIES), 1); _pagefrom = msk.get_int(F_FROMPAGE); _pageto = msk.get_int(F_TOPAGE); } else return false; } else { _pagefrom = pagefrom; _pageto = pageto; _copies = max(copies, 1); } bool ok = false; if (p.printtype() == acrobatprinter) { TFilename f; f.temp(NULL, "pdf"); _pdf_file = f; ok = xvt_print_start_thread(main_loop_callback, (long)this) != 0; if (ok && fsize(f) > 0) printer().acrobatically_print_pdf(f); f.fremove(); } else { _pdf_file.cut(0); ok = xvt_print_start_thread(main_loop_callback, (long)this) != 0; } return ok; } bool TBook::export_pdf(TFilename& filename, bool signature) { bool ok = (pages() > 0) && main_app().has_module(RSAUT); // Controllo paranoico dei permessi if (ok) { // Evita problemi di aggiornamento del pdf: deve sempre rigeneralo! if (filename.exist() && !filename.fremove()) return error_box(FR("Impossibile riscrivere %s"), (const char*)filename); _pdf_file = filename; _pagefrom = 1; _pageto = 0; _copies = 1; ok = xvt_print_start_thread(main_loop_callback, (long)this) != 0; if (ok) ok = fsize(filename) > 0; if (ok && signature && main_app().has_module(FDAUT)) // Controllo paranoico dei permessi { char outfile[_MAX_PATH] = ""; if (xvt_sign_file(filename, outfile) && filename != outfile) { filename.fremove(); // Sbatto via il documento originale non firmato filename = outfile; } } } return ok; } bool TBook::esporta() { if (pages() <= 0) return false; split_file_if_needed(); TFilename fname; const KEY key = spotlite_ask_name(fname); bool ok = isalpha(key) != 0; const bool signature = key >= 'a'; switch (key) { case 'A': case 'a': ok = archive(NULL, signature); break; case 'E': case 'e': ok = export_text(fname, signature); if (ok) xvt_sys_goto_url(fname, "open"); break; case 'M': case 'm': ok = send_mail(fname, signature); break; case 'P': case 'p': ok = export_pdf(fname, signature); if (ok) xvt_sys_goto_url(fname, "open"); break; case 'X': case 'x': ok = export_excel(fname, signature); if (ok) xvt_sys_goto_url(fname, "open"); break; default: break; } return ok; } bool TBook::archive(const char* repname, bool signature) { TFilename pdf; bool ok = spotlite_generate_name(repname, pdf); if (ok) { ok = export_pdf(pdf, signature); if (ok) spotlite_notify(pdf); } return ok; } bool TBook::send_mail(TFilename& file, bool signature) { bool ok = export_pdf(file, signature); if (ok) ok = spotlite_send_mail(file); return ok; } bool TBook::preview() { split_file_if_needed(); TPreview_mask msk(this); // gestione anteprima di stampa;se si ha l'anteprima,una volta lanciata la maschera di stampa, si torna // all'anteprima e non alla maschera del programma (decisivo per stampe con elaborazione lunga) bool go_on = true; while (go_on) { const KEY k = msk.run(); switch (k) { case K_QUIT: go_on = false; break; case K_SAVE: esporta(); break; default : print(); break; } } return true; } bool TBook::print_or_preview() { bool ok = true; switch (printer().printtype()) { case screenvis: ok = preview(); break; case fileprinter: case exportprinter: { TFilename f = printer().get_export_file(); ok = export_text(f, false); if (ok) xvt_sys_goto_url(f, "open"); } break; case acrobatprinter: { TFilename f; f.tempdir(); f.add("tmp.pdf"); if (export_pdf(f, false)) { printer().acrobatically_print_pdf(f); f.fremove(); } } break; default: ok = print(); break; } return ok; } TBook::TBook(const char* name) : _out(NULL), _is_temporary(false), _max_frame(0,0), _pages(0), _page(0), _rcd(NULL), _printwin(NULL), _page_is_open(false) { _file = name; if (_file.blank()) { _file.temp("rep", "rap"); _is_temporary = true; } } TBook::~TBook() { close_output(); if (_is_temporary) xvt_fsys_remove_file(_file); if (_printwin != NULL) delete _printwin; } /////////////////////////////////////////////////////////// // TPrintind /////////////////////////////////////////////////////////// class TPrintind : public TProgind { TArray _modules; protected: virtual void update_bar(); virtual void get_txt_rct(RCT& r) const; public: TPrintind(long n, const char* msg); }; void TPrintind::get_txt_rct(RCT& r) const { TProgind::get_txt_rct(r); r.right -= 48; // Lascia posto all'animazione } void TPrintind::update_bar() { TProgind::update_bar(); const int m = _modules.items(); if (m > 0) // come dire ADVANCED_GRAPHICS { const int n = _status * 100 / _max; const TImage& img = (const TImage&)_modules[n%m]; RCT r; get_bar_rct(r); img.draw(win(), r.right - img.width(), r.top - img.height() - 2); } } TPrintind::TPrintind(long n, const char* msg) : TProgind(n, msg, true, true) { if (ADVANCED_GRAPHICS) { for (int i = 0; i < 4; i++) { TImage* img = new TImage(BMP_MODULE1 + i); img->convert_transparent_color(MASK_BACK_COLOR); _modules.add(img); } } } /////////////////////////////////////////////////////////// // TReport_book /////////////////////////////////////////////////////////// void TReport_book::define_frame(const TReport_rct& rr) { TReport_rct ro(rr); ro += _delta; // Applico offset al rettangolo logico TBook::define_frame(ro); // Salvo le coordinate a 720 DPI } long TReport_book::print_section(char type, int level) { long h = -3; // Hidden TReport_section* rs = _report->find_section(type, level); if (rs != NULL) h = print_section(*rs); return h; } bool TReport_book::open_page() { if (!TBook::open_page()) return false; _report->set_page(++_rep_page, page()); _page_break_allowed = false; _delta.reset(); TReport_section* page_background = _report->find_section('B', 0); if (page_background != NULL) { const TReport_pnt& pos = page_background->pos(); _delta.x = pos.x; _delta.y = pos.y; print_section(*page_background); _delta.reset(); } if (_rep_page == 1) { const long height = print_section('H', 1); if (height > 0) _delta.y += height; } TReport_section* page_head = _report->find_section('H', 0); if (page_head != NULL && (_rep_page > 1 || !page_head->hidden_if_needed())) { const TReport_pnt& pos = page_head->pos(); _delta.x += pos.x; _delta.y += pos.y; const long height = print_section(*page_head); if (height > 0) { _delta.y += height; _delta.x = 0; } } return true; } bool TReport_book::close_page() { if (_page_is_open) { TReport_section* page_foot = _report->find_section('F', 0); if (page_foot != NULL && (!_is_last_page || !page_foot->hidden_if_needed())) { _delta.x = page_foot->pos().x; _delta.y = _logical_foot_pos; print_section(*page_foot); } } return TBook::close_page(); } void TReport_book::reprint_group_headers(const TReport_section& rs) { int max_group = _report->find_max_level('H'); if (rs.type() == 'H' && rs.level() <= max_group) max_group = rs.level()-1; for (int level = 2; level <= max_group; level++) { TReport_section& rs = _report->section('H', level); if (rs.repeat_on_page()) { const long height = rs.compute_size().y; // Compute size after the initilization script! if (height > 0) { rs.print(*this); _delta.y += height; } } } if (rs.level() > 10) // E' una sottosezione ? { TPointer_array headers; // Elenco degli header di sottosezione che devo stampare for (int level = rs.level(); level > 10; level /= 10) { TReport_section& rs = _report->section('H', level); if (rs.repeat_on_page()) { const long height = rs.compute_size().y; // Compute size after the initilization script! if (height > 0) headers.add(rs); } } for (int i = headers.last(); i >= 0; i--) // Stampo in ordine livello { TReport_section& rs = (TReport_section&)headers[i]; const long height = rs.compute_size().y; rs.print(*this); _delta.y += height; } } } // Stampa una sezione e ne ritorna l'altezza. // In caso di problemi particolari torna un'altezza negativa: // -1 Abort // -2 Condizione di stampa non verificata // -3 Nascosta long TReport_book::print_section(TReport_section& rs) { if (_print_aborted) return -1; rs.load_fields(); rs.init_dynamic_heights(*this); // Non sono sicuro se vada prima di load_fields o dopo execute_prescript if (rs.condition().full()) { TVariant var; _report->evaluate(rs.condition(), var, _alfafld); if (!var.as_bool()) return -2; } if (!rs.execute_prescript(*this)) { _print_aborted = true; return -1; } if (rs.hidden()) { if (!rs.execute_postscript()) // Faccio ugualmente i post-script _print_aborted = true; return -3; } const long height = rs.compute_size().y; // Compute size after the initilization script! if (height > 0) // Has some visible fields { long reprint_from = 0; // Posizione di stampa per sezione interrotte a meta' bool page_break = _page_break_allowed && rs.page_break(); // Salto pagina "forzato" if (!page_break && rs.level() > 0) // Controlla se il salto pagina e' "necessario" { long h = height; if (rs.keep_with_next()) // Devo mantenermi con la prossima sezione { char next_type = 'B'; // Spesso la prossima sezione e' body 1 int next_level = 1; if (rs.type() == 'H') // Tento di essere piu' preciso con gli headers { const int maxlev = rs.report().find_max_level('H'); if (rs.level() < maxlev) { next_type = 'H'; next_level = rs.level()+1; h += 100; // Bonus per il body successivo all'header successivo } } const TReport_section& next_section = rs.report().section(next_type, next_level); if (next_section.page_break()) h += _page_size.y; else h += next_section.compute_size().y; } const long space_left = (_logical_foot_pos - _delta.y)/100*100; // Calcola spazio rimasto page_break = h > space_left; // Controlla se e' sufficiente // Controllo se la sezione puo' essere stampata su due pagine if (page_break && space_left > 100 && rs.can_be_broken()) { reprint_from = space_left; rs.print_clipped(*this, 0, reprint_from); } } if (page_break) { close_page(); open_page(); reprint_group_headers(rs); } if (_page_is_open) { if (reprint_from > 0) { _delta.y -= reprint_from; rs.print_clipped(*this, reprint_from, height); } else rs.print(*this); } if (rs.level() > 0 && (rs.type() != 'H' || rs.level() > 1)) // Ho stampato qualcosa che non fosse lo sfondo! _page_break_allowed = true; } if (!rs.execute_postscript()) { _print_aborted = true; return -1; } return height; } bool TReport_book::init(TReport& rep) { TPrinter& pr = printer(); bool save_profile = rep.save_last_printer() && (pr.printtype() < exportprinter); if (save_profile && !pr.manual_setup()) { const TString profile = rep.filename().name(); const TString& rep_printer = ini_get_string(CONFIG_STAMPE, profile, "Name"); const TString& cur_printer = pr.printername(); if (rep_printer.full() && rep_printer != cur_printer) { pr.read_configuration(profile); // Uso la stampante preferita dal report save_profile = false; } } // Controlla orientamento della carta prima di inizializzare const int report_orientation = rep.orientation(); if (report_orientation > 0) { pr.set_landscape_orientation(report_orientation == 2); // I parametri verranno riletti dalla TBook::init() } if (save_profile) { const TString profile = rep.filename().name(); pr.save_configuration(profile); } if (!TBook::init()) return false; _report = &rep; if (rep.use_printer_font()) rep.load_printer_font(); const TSize siz = page_size(); const TSize res = page_res(); const double pollici_pagina_y = (double)siz.y / (double)res.y; const double righe_pagina = pollici_pagina_y * lpi(); _logical_page_height = long(righe_pagina*100.0); const double pollici_pagina_x = (double)siz.x / (double)res.x; const double colonne_pagina = pollici_pagina_x * cpi(); _logical_page_width = long(colonne_pagina*100.0); const TReport_section& footer = _report->section('F', 0); _logical_foot_pos = footer.pos().y; if (_logical_foot_pos <= 0) { const long logical_footer_height = footer.compute_size().y; _logical_foot_pos = _logical_page_height - logical_footer_height; } return true; } void TReport_book::print_subsections(int father) { for (int i = 1; i <= 9 && !_print_aborted; i++) { const int level = father*10+i; TReport_section* rs = _report->find_section('B', level); if (rs == NULL) break; TRecordset* rex = rs->recordset(); if (rex != NULL) { const TString_array& variables = rex->variables(); FOR_EACH_ARRAY_ROW(variables, v, name) if (!name->starts_with("#PARENT")) { const TReport_field* fld = _report->field(*name); if (fld != NULL) { const TVariant& val = fld->get(); rex->set_var(*name, val); } } rex->requery(); if (rex->items() > 0) { long height = print_section('H', level); if (height > 0) _delta.y += height; for (bool ok = rex->move_to(0); ok && !_print_aborted; ok = rex->move_next()) { height = print_section('B', level); if (height >= 0) { _delta.y += height; print_subsections(level); } } height = print_section('F', level); if (height > 0) _delta.y += height; } } } } void TReport_book::add_doc(const TString& name) { if (name.ends_with(".rep", true)) // Tratto a parte i report! { TReport* eminem = new TReport; if (eminem->load(name)) { TReport* rep = _report; // Salvo variabile globale if (rep->use_mask()) { TFilename msk = rep->filename().name(); msk.ext("msk"); if (msk.custom_path()) { TMask m(msk); rep->report2mask(m); eminem->mask2report(m); } } add(*eminem, true); _report = rep; // Ripristino variabile globale } delete eminem; } else TBook::add_doc(name); } bool TReport_book::add(TReport& rep, bool progind) { if (!init(rep)) return false; if (!_report->execute_prescript()) return false; TRecordset* rex = _report->recordset(); if (rex == NULL) return true; rex->requery(); const TRecnotype rex_items = rex->items(); if (rex_items <= 0) return true; TString msg; msg << TR("Elaborazione report ") << _report->filename(); TProgind* pi = NULL; if (progind && rex_items > 1) pi = new TPrintind(rex_items, msg); TString_array oldgroup, newgroup; const int max_group = _report->find_max_level('H'); if (max_group >= 2) { for (int g = 2; g <= max_group; g++) oldgroup.add(EMPTY_STRING, g); } const int max_body = _report->find_max_level('B'); int last_body_height = 0; _rep_page = rep.last_printed_page(); // Azzera numero di pagina relativo _is_last_page = false; bool ok = rex->move_first(); open_page(); for (; ok && !_print_aborted; ok = rex->move_next()) { if (max_group >= 2) // Gestione raggruppamenti { int first_changed = 0; TVariant var; for (int g = 2; g <= max_group; g++) { const TString& expr = _report->section('H', g).grouped_by(); _report->evaluate(expr, var, _alfafld); const TString& grp = var.as_string(); newgroup.add(grp, g); if (newgroup.row(g) != oldgroup.row(g) || rex->current_row() == 0) { if (first_changed == 0) first_changed = g; } } if (first_changed) { oldgroup = newgroup; if (_delta.x > 0) // Devo tornare a capo! _delta.y += last_body_height; _delta.x = 0; if (rex->current_row() > 0) { for (int g = max_group; g >= first_changed ; g--) { const long height = print_section('F', g); if (height > 0) _delta.y += height; } } for (int g = first_changed; g <= max_group; g++) { const long height = print_section('H', g); if (height > 0) _delta.y += height; } } } // Stampa di tutti i body for (int b = 1; b <= max_body; b++) { const int dy = print_section('B', b); if (dy > 0) // Ho stampato fisicamente qualcosa { // Cerco di vedere se e' possibile la stampa etichette int column_delta = 0; const int dx = _report->section('B', b).size().x; // Se dx > 0 ho una sezione a dimensione fissa if (dx > 0 && _delta.x+2*dx <= _logical_page_width) { column_delta = dx; last_body_height = dy; } if (column_delta > 0) _delta.x += column_delta; else { _delta.x = 0; _delta.y += dy; last_body_height = 0; // Non servirebbe strettamente } } if (dy >= 0) { // Stampa eventuali sottosezioni print_subsections(b); } } if (pi != NULL) { if (!pi->addstatus(1)) _print_aborted = true; } } if (!_print_aborted) { // Devo stampare tutte le code degli eventuali raggrupamenti for (int g = max_group; g >= 2 ; g--) { const long height = print_section('F', g); if (height > 0) _delta.y += height; } TReport_section* fl = _report->find_section('F',1); if (fl != NULL) // Gestione footer last (se esite) { const int fy = fl->pos().y; if (fy > 0) // Ha una coordinata y imposta { if (fy < _delta.y) // Sono gia' andato oltre quindi salto pagina { close_page(); open_page(); } _delta.x = 0; _delta.y = fy; // Azzero temporaneamente le dimensioni del footer per evitare salti pagina const int lfp = _logical_foot_pos; _logical_foot_pos = _logical_page_height; print_section(*fl); _logical_foot_pos = lfp; } else print_section(*fl); // Stampa normale } _is_last_page = true; close_page(); _report->execute_postscript(); if (!_print_aborted) { const TString_array& all = _report->allegates(); for (int a = 0; a < all.items(); a++) { TFilename name = all.row(a); if (name.find('#') >= 0) // Se puo' essere un'espressione { TVariant var; if (_report->evaluate(name, var, _alfafld)) name = var.as_string(); } add_doc(name); } } } if (pi != NULL) delete pi; return !_print_aborted; } int TReport_book::lpi() const { if (_report != NULL) return _report->print_lpi(); return TBook::lpi(); } int TReport_book::cpi() const { if (_report != NULL) return _report->print_cpi(); return TBook::cpi(); } bool TReport_book::can_split(int pages) const { if (_report != NULL) return _report->page_split_allowed(); return TBook::can_split(pages); } bool TReport_book::can_merge(int pages) const { if (_report != NULL) return _report->page_merge_allowed(); return TBook::can_merge(pages); } bool TReport_book::print(size_t pagefrom, size_t pageto, word copies) { if (pages() <= 0) return false; split_file_if_needed(); if (pagefrom <= 0) { TPrinter& p = printer(); TMask msk("bagn003"); msk.set(F_PRINTER, p.printername()); if (_report != NULL) { msk.set(F_FORM, _report->filename()); msk.set(F_FONT, _report->print_font().name()); msk.set(F_SIZE, _report->print_font().size()); } else { msk.set(F_FONT, p.fontname()); msk.set(F_SIZE, p.get_char_size()); } msk.set(F_LINES, lpi()); msk.set(F_ORIENT, p.is_portrait() ? 1 : 2); msk.set(F_ISGRAPHICS, p.isgraphics()); msk.set(F_FROMPAGE, 1); msk.set(F_TOPAGE, pages()); msk.set(F_COPIES, 1); if (msk.run() == K_ENTER) { copies = msk.get_int(F_COPIES); pagefrom = msk.get_int(F_FROMPAGE); pageto = msk.get_int(F_TOPAGE); } else return false; } return TBook::print(pagefrom, pageto, copies); } bool TReport_book::archive(const char* repname, bool signature) { TFilename n = repname; if (n.blank() && _report != NULL) { n = _report->filename().name(); n.ext(""); } return TBook::archive(n, signature); } bool TReport_book::on_link(const TReport_link& lnk) { bool ok = false; if (_report != NULL) ok = _report->on_link(lnk); return ok; } TReport_book::TReport_book(const char* name) : TBook(name), _report(NULL) { }