/* * Copyright (c) 1990, 1991 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Stanford not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Stanford makes no representations about * the suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Text component definitions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*****************************************************************************/ ClassId TextComp::GetClassId () { return TEXT_COMP; } boolean TextComp::IsA (ClassId id) { return TEXT_COMP == id || GraphicComp::IsA(id); } Component* TextComp::Copy () { return new TextComp((TextGraphic*) GetGraphic()->Copy()); } TextComp::TextComp (TextGraphic* graphic) : GraphicComp(graphic) { } void TextComp::Interpret (Command* cmd) { TextGraphic* gr = (TextGraphic*) GetGraphic(); if (cmd->IsA(BRUSH_CMD) || cmd->IsA(PATTERN_CMD)) { // do nothing } else if (cmd->IsA(FONT_CMD)) { PSFont* font = ((FontCmd*) cmd)->GetFont(); cmd->Store(this, new VoidData(gr->GetFont())); gr->SetFont(font); gr->SetLineHeight(font->GetLineHt()); // hack; should be state var Notify(); } else { GraphicComp::Interpret(cmd); } } void TextComp::Uninterpret (Command* cmd) { Graphic* gr = GetGraphic(); if (cmd->IsA(BRUSH_CMD) || cmd->IsA(PATTERN_CMD)) { // do nothing } else if (cmd->IsA(FONT_CMD)) { VoidData* vd = (VoidData*) cmd->Recall(this); PSFont* font = (PSFont*) vd->_void; gr->SetFont(font); Notify(); } else { GraphicComp::Uninterpret(cmd); } } TextGraphic* TextComp::GetText () { return (TextGraphic*) GetGraphic(); } void TextComp::Read (istream& in) { GraphicComp::Read(in); int lineHt; in >> lineHt; char* string = ReadString(in); TextGraphic* text = new TextGraphic(string, lineHt); delete[] string; text->FillBg(ReadBgFilled(in)); PSColor* fg = ReadColor(in); PSColor* bg = ReadColor(in); text->SetColors(fg, bg); text->SetFont(ReadFont(in)); Transformer* t = ReadTransformer(in); text->SetTransformer(t); Unref(t); SetGraphic(text); } void TextComp::Write (ostream& out) { GraphicComp::Write(out); TextGraphic* text = GetText(); out << text->GetLineHeight() << "\n"; WriteString(text->GetOriginal(), out); WriteBgFilled(text->BgFilled(), out); WriteColor(text->GetFgColor(), out); WriteColor(text->GetBgColor(), out); WriteFont(text->GetFont(), out); WriteTransformer(text->GetTransformer(), out); } /****************************************************************************/ TextComp* TextView::GetTextComp () { return (TextComp*) GetSubject(); } ClassId TextView::GetClassId () { return TEXT_VIEW; } boolean TextView::IsA (ClassId id) { return TEXT_VIEW == id || GraphicView::IsA(id); } TextView::TextView (TextComp* subj) : GraphicView(subj) { } boolean TextView::TextChanged () { TextGraphic* gview = (TextGraphic*) GetGraphic(); TextGraphic* gsubj = (TextGraphic*) GetTextComp()->GetGraphic(); return *gview != *gsubj; } void TextView::Interpret (Command* cmd) { if (cmd->IsA(ALIGNTOGRID_CMD)) { Transformer total; GetGraphic()->TotalTransformation(total); float tx0, ty0; total.Transform(0., 0., tx0, ty0); ((AlignToGridCmd*) cmd)->Align(this, tx0, ty0); } else { GraphicView::Interpret(cmd); } } void TextView::Update () { TextGraphic* gview = (TextGraphic*) GetGraphic(); TextGraphic* gsubj = (TextGraphic*) GetTextComp()->GetGraphic(); IncurDamage(gview); * (Graphic*) gview = * (Graphic*) gsubj; gview->SetLineHeight(gsubj->GetLineHeight()); IncurDamage(gview); EraseHandles(); } Manipulator* TextView::CreateManipulator ( Viewer* v, Event& e, Transformer* rel, Tool* tool ) { Manipulator* m = nil; Editor* ed = v->GetEditor(); int tabWidth = Math::round(.5*ivinch); if (tool->IsA(GRAPHIC_COMP_TOOL)) { FontVar* fontVar = (FontVar*) ed->GetState("FontVar"); ColorVar* colVar = (ColorVar*) ed->GetState("ColorVar"); PSFont* font = (fontVar == nil) ? psstdfont : fontVar->GetFont(); PSColor* fg = (colVar == nil) ? psblack : colVar->GetFgColor(); int lineHt = font->GetLineHt(); Painter* painter = new Painter; painter->FillBg(false); painter->SetFont(font); painter->SetColors(fg, nil); Orientation o = v->GetOrientation(); if (o!=Rotated) painter->SetTransformer(rel); else { rel = new Transformer(rel); rel->Rotate(90.0); painter->SetTransformer(rel); Unref(rel); } m = new TextManip(v, painter, lineHt, tabWidth, tool); } else if (tool->IsA(RESHAPE_TOOL)) { TextGraphic* textgr = (TextGraphic*) GetGraphic(); Painter* painter = new Painter; int lineHt = textgr->GetLineHeight(); Coord xpos, ypos; rel = new Transformer; const char* text = textgr->GetOriginal(); int size = strlen(text); textgr->TotalTransformation(*rel); rel->Transform(0, 0, xpos, ypos); painter->FillBg(false); painter->SetFont(textgr->GetFont()); painter->SetColors(textgr->GetFgColor(), nil); painter->SetTransformer(rel); Unref(rel); m = new TextManip( v, text, size, xpos, ypos, painter, lineHt, tabWidth, tool ); } else { m = GraphicView::CreateManipulator(v, e, rel, tool); } return m; } Command* TextView::InterpretManipulator (Manipulator* m) { Viewer* v = m->GetViewer(); Editor* ed = v->GetEditor(); Tool* tool = m->GetTool(); Command* cmd = nil; if (tool->IsA(GRAPHIC_COMP_TOOL) || tool->IsA(RESHAPE_TOOL)) { TextManip* tm = (TextManip*) m; int size; const char* text = tm->GetText(size); if (size == 0) { if (tool->IsA(RESHAPE_TOOL)) { cmd = new DeleteCmd(ed); } else { v->Update(); // to repair text display-incurred damage } } else { Coord xpos, ypos; tm->GetPosition(xpos, ypos); Painter* p = tm->GetPainter(); Transformer* rel = tm->GetPainter()->GetTransformer(); int lineHt = tm->GetLineHeight(); Graphic* pg = GetGraphicComp()->GetGraphic(); TextGraphic* textgr = new TextGraphic(text, lineHt, pg); if (tool->IsA(GRAPHIC_COMP_TOOL)) { textgr->SetTransformer(nil); } if (rel != nil) { if (v->GetOrientation()==Rotated && !tool->IsA(RESHAPE_TOOL)) rel->Rotate(-90); rel->InvTransform(xpos, ypos); } if (v->GetOrientation()==Rotated && !tool->IsA(RESHAPE_TOOL)) textgr->Rotate(90.0); textgr->Translate(xpos, ypos); textgr->FillBg(false); textgr->SetFont((PSFont*) p->GetFont()); textgr->SetColors((PSColor*) p->GetFgColor(), nil); if (tool->IsA(GRAPHIC_COMP_TOOL)) { cmd = new PasteCmd(ed, new Clipboard(new TextComp(textgr))); } else { cmd = new ReplaceCmd(ed, new TextComp(textgr)); } } } else { cmd = GraphicView::InterpretManipulator(m); } return cmd; } Graphic* TextView::GetGraphic () { TextComp* textComp; Graphic* graphic = GraphicView::GetGraphic(); if (graphic == nil) { textComp = GetTextComp(); graphic = textComp->GetGraphic()->Copy(); SetGraphic(graphic); } return graphic; } /****************************************************************************/ TextGraphic::TextGraphic (const char* s, int h, Graphic* gr) : ULabel(s, gr) { _lineHt = h; } TextGraphic::TextGraphic (const char* s, Graphic* gr) : ULabel(s, gr) { _lineHt = gr->GetFont()->Height(); } boolean TextGraphic::operator == (TextGraphic& tg) { const char* tgstring = tg.GetOriginal(); int tgcount = strlen(tgstring); int count = strlen(_string); return (tgcount == count) ? strcmp(tgstring, _string) == 0 : false; } boolean TextGraphic::operator != (TextGraphic& tg) { return !(*this == tg); } Graphic* TextGraphic::Copy () { return new TextGraphic(_string, _lineHt, this); } ClassId TextGraphic::CompId() { return TEXT_COMP; } void TextGraphic::SetLineHeight (int h) { _lineHt = h; } int TextGraphic::GetLineHeight () { return _lineHt; } void TextGraphic::draw (Canvas* c, Graphic* gs) { int beg, end, lineSize, nextBeg, ypos = 0; int count = strlen(_string); gs->SetPattern(psnonepat); gs->SetBrush(psnonebr); update(gs); for (beg = 0; beg < count; beg = nextBeg) { GetLine(_string, count, beg, end, lineSize, nextBeg); _p->Text(c, &_string[beg], lineSize, 0, ypos); ypos -= _lineHt; } } void TextGraphic::getExtent ( float& x0, float& y0, float& cx, float& cy, float& tol, Graphic* gs ) { PSFont* f = gs->GetFont(); Coord l, b, r, t; CalcBox(l, b, r, t, f); if (gs->GetTransformer() == nil) { x0 = l; y0 = b; cx = float(l + r) / 2; cy = float(b + t) / 2; } else { transformRect(l, b, r, t, x0, y0, cx, cy, gs); cx = (cx + x0)/2; cy = (cy + y0)/2; } tol = 0; } void TextGraphic::CalcBox (Coord& l, Coord& b, Coord& r, Coord& t, PSFont* f) { int beg, end, lineSize, nextBeg; const char* s = GetOriginal(); int size = strlen(s); l = r = 0; b = _lineHt; t = f->Height(); for (beg = 0; beg < size; beg = nextBeg) { GetLine(s, size, beg, end, lineSize, nextBeg); r = max(r, f->Width(&s[beg], lineSize) - 1); b -= _lineHt; } } boolean TextGraphic::contains (PointObj& po, Graphic* gs) { PointObj pt (&po); PSFont* f = gs->GetFont(); BoxObj box(0, 0, 0, f->Height()); int beg, end, lineSize, nextBeg, ypos = 0; const char* s = GetOriginal(); int size = strlen(s); invTransform(pt._x, pt._y, gs); for (beg = 0; beg < size; beg = nextBeg) { GetLine(s, size, beg, end, lineSize, nextBeg); box._right = f->Width(&s[beg], lineSize) - 1; if (box.Contains(pt)) { return true; } box._top -= _lineHt; box._bottom -= _lineHt; } return false; } boolean TextGraphic::RotatedIntersects (BoxObj& userb, Graphic* gs) { int beg, end, lineSize, nextBeg, ypos = 0; const char* s = GetOriginal(); int size = strlen(s); PSFont* f = gs->GetFont(); Coord x[4], tx[5]; Coord y[4], ty[5]; x[0] = x[1] = x[2] = x[3] = y[0] = y[1] = 0; y[2] = y[3] = f->Height(); for (beg = 0; beg < size; beg = nextBeg) { GetLine(s, size, beg, end, lineSize, nextBeg); x[1] = x[2] = f->Width(&s[beg], lineSize) - 1; transformList(x, y, 4, tx, ty, gs); tx[4] = tx[0]; ty[4] = ty[0]; FillPolygonObj fp(tx, ty, 5); if (fp.Intersects(userb)) { return true; } y[0] -= _lineHt; y[1] -= _lineHt; y[2] -= _lineHt; y[3] -= _lineHt; } return false; } boolean TextGraphic::TransformedIntersects (BoxObj& userb, Graphic* gs) { int beg, end, lineSize, nextBeg, ypos = 0; const char* s = GetOriginal(); int size = strlen(s); PSFont* f = gs->GetFont(); Coord l, b, r, t = f->Height(); Coord tl, tb, tr, tt; l = b = r = 0; for (beg = 0; beg < size; beg = nextBeg) { GetLine(s, size, beg, end, lineSize, nextBeg); r = f->Width(&s[beg], lineSize) - 1; transform(l, b, tl, tb, gs); transform(r, t, tr, tt, gs); BoxObj box(tl, tb, tr, tt); if (box.Intersects(userb)) { return true; } b -= _lineHt; t -= _lineHt; } return false; } boolean TextGraphic::UntransformedIntersects (BoxObj& userb, Graphic* gs) { int beg, end, lineSize, nextBeg, ypos = 0; const char* s = GetOriginal(); int size = strlen(s); PSFont* f = gs->GetFont(); BoxObj box(0, 0, 0, f->Height()); for (beg = 0; beg < size; beg = nextBeg) { GetLine(s, size, beg, end, lineSize, nextBeg); box._right = f->Width(&s[beg], lineSize) - 1; if (box.Intersects(userb)) { return true; } box._top -= _lineHt; box._bottom -= _lineHt; } return false; } boolean TextGraphic::intersects (BoxObj& userb, Graphic* gs) { Transformer* t = gs->GetTransformer(); boolean intersects; if (t != nil && t->Rotated()) { intersects = RotatedIntersects(userb, gs); } else if (t != nil) { intersects = TransformedIntersects(userb, gs); } else { intersects = UntransformedIntersects(userb, gs); } return intersects; } /****************************************************************************/ ClassId PSText::GetClassId () { return PS_TEXT; } boolean PSText::IsA (ClassId id) { return PS_TEXT == id || PostScriptView::IsA(id); } PSText::PSText (TextComp* subj) : PostScriptView(subj) { } boolean PSText::Definition (ostream& out) { TextComp* comp = (TextComp*) GetSubject(); TextGraphic* g = comp->GetText(); const char* text = g->GetOriginal(); int count = strlen(text); out << "Begin " << MARK << " Text\n"; float sep = g->GetLineHeight() - 1; // correct for vert shift Transformer corrected, *t = g->GetTransformer(); corrected.Translate(0., sep); if (t == nil) { g->SetTransformer(&corrected); TextGS(out); g->SetTransformer(t); } else { t->Reference(); corrected.Postmultiply(t); g->SetTransformer(&corrected); TextGS(out); g->SetTransformer(t); Unref(t); } out << MARK << "\n"; out << "[\n"; int beg, end, lineSize, nextBeg, ypos = 0; for (beg = 0; beg < count; beg = nextBeg) { GetLine(text, count, beg, end, lineSize, nextBeg); const char* string = Filter(&text[beg], end - beg + 1); out << "(" << string << ")\n"; } out << "] Text\n"; out << "End\n\n"; return out.good(); } // octal converts a character to the string \ddd where d is an octal digit. static char* octal(unsigned char c, register char* p) { *p-- = '\0'; // backwards from terminating null... *p-- = (char)('0' + c%8); *p-- = (char)('0' + (c >>= 3)%8); *p-- = (char)('0' + (c >>= 3)%8); *p = '\\'; // ...to beginning backslash return p; } // Filter escapes embedded special characters that would cause syntax // errors in a Postscript string. const char* PSText::Filter (const char* string, int len) { TextBuffer stext(sbuf, 0, SBUFSIZE); int dot; for (dot = 0; len--; string++) { char c = *string; if (!isascii(c) || iscntrl(c)) { char buf[5]; octal(c, &buf[sizeof(buf) - 1]); dot += stext.Insert(dot, buf, sizeof(buf) - 1); } else { switch (c) { case '(': case ')': case '\\': dot += stext.Insert(dot, "\\", 1); // fall through default: dot += stext.Insert(dot, string, 1); } } } stext.Insert(dot, "", 1); return stext.Text(); }