Link destionation includes rectangle on page.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3695 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Pfeiffer 2003-06-27 17:19:35 +00:00
parent 61594003dd
commit 2e07400817
5 changed files with 154 additions and 113 deletions

View File

@ -37,53 +37,43 @@ THE SOFTWARE.
#include "Log.h"
#include "Report.h"
Link::Link(PDFWriter* writer, BString* utf8, BFont* font)
Link::Link(PDFWriter* writer, BString* utf8)
: fWriter(writer)
, fUtf8(utf8)
, fFont(font)
, fPos(0)
, fContainsLink(false)
{
}
bool
Link::BoundingBox(BRect* rect) {
return fWriter->fTextLine.BoundingBox(fStartPos, fEndPos+1, rect);
}
// create link from fStartPos to fEndPos and fStart to end
void
Link::CreateLink(BPoint end)
Link::CreateLink()
{
if (fFont->Rotation() != 0.0) {
static bool reported = false; // report warning once only
BRect bounds;
if (BoundingBox(&bounds)) {
CreateLink(bounds.left, bounds.bottom, bounds.right, bounds.top);
} else if (!reported) {
REPORT(kWarning, fWriter->fPage, "Warning: Can not create link for rotated font!");
return;
reported = true;
}
// calculate rectangle for url
font_height height;
fFont->GetHeight(&height);
float llx, lly, urx, ury;
llx = fWriter->tx(fStart.x);
urx = fWriter->tx(end.x);
lly = fWriter->ty(fStart.y + height.descent);
ury = fWriter->ty(end.y - height.ascent);
CreateLink(llx, lly, urx, ury);
}
void
Link::NextChar(int cps, float x, float y, float w)
{
if (fContainsLink) {
if (fPos == fStartPos) {
fStart.Set(x, y);
Link::Do() {
int pos = 0;
do {
DetectLink(pos);
if (ContainsLink()) {
CreateLink();
pos = fEndPos;
}
if (fPos == fEndPos) {
CreateLink(BPoint(x + w, y));
DetectLink(fPos + cps);
}
fPos += cps;
}
} while (ContainsLink());
}
// TODO: check this list and add more prefixes
@ -292,8 +282,8 @@ WebLink::CreateLink(float llx, float lly, float urx, float ury)
}
WebLink::WebLink(PDFWriter* writer, BString* utf8, BFont* font)
: Link(writer, utf8, font)
WebLink::WebLink(PDFWriter* writer, BString* utf8)
: Link(writer, utf8)
{
}
@ -371,6 +361,78 @@ bool TextLine::Follows(TextSegment* segment) const {
return false;
}
bool TextLine::BoundingBox(int startPos, int endPos, BRect* bounds) {
const int32 n = fSegments.CountItems();
if (n == 0) return false;
// build the text in the line
BString line;
for (int32 i = 0; i < n; i ++) {
line << fSegments.ItemAt(i)->Text();
}
const char* c = line.String();
const char* pStart = &c[startPos];
const char* pEnd = &c[endPos];
for (int32 i = 0; i < n; i ++) {
TextSegment* seg = fSegments.ItemAt(i);
BPoint pos = seg->Start();
BFont* font = seg->Font();
const char* sc = seg->Text();
float escpSpace = seg->EscpSpace();
float escpNoSpace = seg->EscpNoSpace();
int32 spaces = seg->Spaces();
PDFSystem* system = seg->System();
while (*sc != 0) {
ASSERT(*sc == *c);
int s = fWriter->CodePointSize((char*)c);
float w = font->StringWidth(c, s);
if (c == pStart) {
bounds->left = bounds->right = system->tx(pos.x);
bounds->top = bounds->bottom = system->ty(pos.y);
}
if (pStart <= c && c < pEnd) {
font_height height;
font->GetHeight(&height);
float top = system->ty(pos.y - height.ascent);
float bottom = system->ty(pos.y + height.descent);
if (bounds->top < top) bounds->top = top;
if (bounds->bottom > bottom) bounds->bottom = bottom;
bounds->right = system->tx(pos.x+w);
}
if (spaces == 0) {
// position of next character
if (*(unsigned char*)c <= 0x20) { // should test if c is a white-space!
w += escpSpace;
} else {
w += escpNoSpace;
}
pos.x += w;
} else {
// skip over the prepended spaces
spaces --;
}
// next character
c += s; sc += s;
if (c >= pEnd) {
return true;
}
}
}
return false;
}
void TextLine::Flush() {
const int32 n = fSegments.CountItems();
@ -387,65 +449,28 @@ void TextLine::Flush() {
if (!fWriter->MakesPDF()) {
if (fWriter->fCreateXRefs) fWriter->RecordDests(c);
} else {
// XXX
TextSegment* s = fSegments.ItemAt(0);
BFont* f = s->Font();
// simple link handling for now
WebLink webLink(fWriter, &line, f);
if (fWriter->fCreateWebLinks) webLink.Init();
WebLink webLink(fWriter, &line);
if (fWriter->fCreateWebLinks) webLink.Do();
// local links
LocalLink localLink(fWriter->fXRefs, fWriter->fXRefDests, fWriter, &line, f, fWriter->fPage);
if (fWriter->fCreateXRefs) localLink.Init();
LocalLink localLink(fWriter->fXRefs, fWriter->fXRefDests, fWriter, &line, fWriter->fPage);
if (fWriter->fCreateXRefs) localLink.Do();
// simple bookmark adding (XXX: s->Start()
// simple bookmark adding
if (fWriter->fCreateBookmarks) {
BPoint start(s->System()->tx(s->Start().x), s->System()->ty(s->Start().y));
fWriter->fBookmark->AddBookmark(start, c, f);
}
TextSegment* s = fSegments.ItemAt(0);
BFont* f = s->Font();
PDFSystem* system = s->System();
BPoint start(system->tx(s->Start().x), system->ty(s->Start().y));
for (int32 i = 0; i < n; i ++) {
TextSegment* seg = fSegments.ItemAt(i);
BPoint pos = seg->Start();
BFont* font = seg->Font();
const char* sc = seg->Text();
float escpSpace = seg->EscpSpace();
float escpNoSpace = seg->EscpNoSpace();
int32 spaces = seg->Spaces();
while (*sc != 0) {
ASSERT(*sc == *c);
int s = fWriter->CodePointSize((char*)c);
float w = font->StringWidth(c, s);
if (fWriter->fCreateWebLinks) webLink.NextChar(s, pos.x, pos.y, w);
if (fWriter->fCreateXRefs) localLink.NextChar(s, pos.x, pos.y, w);
if (spaces == 0) {
// position of next character
if (*(unsigned char*)c <= 0x20) { // should test if c is a white-space!
w += escpSpace;
} else {
w += escpNoSpace;
}
pos.x += w;
} else {
// skip over the prepended spaces
spaces --;
}
// next character
c += s; sc += s;
}
font_height height;
f->GetHeight(&height);
float h = system->scale(height.ascent);
fWriter->fBookmark->AddBookmark(start, h, c, f);
}
}
fSegments.MakeEmpty();
}

View File

@ -37,26 +37,28 @@ THE SOFTWARE.
class PDFWriter;
class TextLine;
class Link {
protected:
PDFWriter* fWriter;
BString* fUtf8;
BFont* fFont;
int fPos; // into fUtf8
bool fContainsLink;
int fStartPos, fEndPos;
BPoint fStart;
virtual void DetectLink(int start) = 0;
virtual void CreateLink(float llx, float lly, float urx, float ury) = 0;
void CreateLink(BPoint end);
void CreateLink();
bool ContainsLink() const { return fContainsLink; }
private:
bool BoundingBox(BRect* rect);
public:
Link(PDFWriter* writer, BString* utf8, BFont* font);
void Init() { DetectLink(0); }
void NextChar(int cps, float x, float y, float w);
Link(PDFWriter* writer, BString* utf8);
void Do();
};
class WebLink : public Link {
@ -82,7 +84,7 @@ class WebLink : public Link {
void DetectLink(int start);
public:
WebLink(PDFWriter* writer, BString* utf8, BFont* font);
WebLink(PDFWriter* writer, BString* utf8);
};
class TextSegment {
@ -122,6 +124,8 @@ public:
void Add(TextSegment* segment);
void Flush();
// startPos inclusive, endPos exclusive
bool BoundingBox(int startPos, int endPos, BRect* bounds);
};

View File

@ -321,7 +321,7 @@ PDFWriter::CodePointSize(const char* s)
void PDFWriter::RecordDests(const char* s) {
::RecordDests record(fXRefDests, fPage);
::RecordDests record(fXRefDests, &fTextLine, fPage);
fXRefs->Matches(s, &record, true);
}

View File

@ -214,7 +214,7 @@ Line comment starts with: '#'
Definition = Version {XRefDef}.
Version = "CrossReferences" "1.0".
XRefDef = Pattern {"," Pattern} "-" ">" Pattern {, Pattern} ".".
XRefDef = Pattern {"," Pattern} "-" ">" Pattern {"," Pattern} ".".
Pattern = '"' string '"'.
Example:
@ -301,9 +301,10 @@ bool XRefDefs::Read(const char* name) {
// Destination
Destination::Destination(const char* label, int32 page)
Destination::Destination(const char* label, int32 page, BRect bounds)
: fLabel(label)
, fPage(page)
, fBoundingBox(bounds)
{
}
@ -336,23 +337,24 @@ XRefDests::XRefDests(int32 n) {
}
bool XRefDests::Add(XRefDef* def, const char* label, int32 page) {
bool XRefDests::Add(XRefDef* def, const char* label, int32 page, BRect boundingBox) {
DestList* list = GetList(def);
ASSERT(list != NULL);
if (!list->Find(label)) {
list->AddItem(new Destination(label, page));
list->AddItem(new Destination(label, page, boundingBox));
return true;
}
return false;
}
bool XRefDests::Find(XRefDef* def, const char* label, int32* page) const {
bool XRefDests::Find(XRefDef* def, const char* label, int32* page, BRect* boundingBox) const {
DestList* list = GetList(def);
ASSERT(list != NULL);
Destination* dest = list->Find(label);
if (dest) {
*page = dest->Page();
*boundingBox = dest->BoundingBox();
return true;
}
return false;
@ -361,13 +363,13 @@ bool XRefDests::Find(XRefDef* def, const char* label, int32* page) const {
// RecordDests
RecordDests::RecordDests(XRefDests* dests, int32 page)
RecordDests::RecordDests(XRefDests* dests, TextLine* line, int32 page)
: fDests(dests)
, fLine(line)
, fPage(page)
{
}
bool RecordDests::Link(XRefDef* def, MatchResult* result) {
return true;
}
@ -376,8 +378,11 @@ bool RecordDests::Link(XRefDef* def, MatchResult* result) {
bool RecordDests::Dest(XRefDef* def, MatchResult* result) {
if (result->CountResults() >= 2) {
BString s;
BRect b;
result->GetString(1, &s);
fDests->Add(def, s.String(), fPage);
if (fLine->BoundingBox(result->StartPos(1), result->EndPos(1), &b)) {
fDests->Add(def, s.String(), fPage, b);
}
}
return true;
}
@ -385,8 +390,8 @@ bool RecordDests::Dest(XRefDef* def, MatchResult* result) {
// LocalLink
LocalLink::LocalLink(XRefDefs* defs, XRefDests* dests, PDFWriter* writer, BString* utf8, BFont* font, int32 page)
: ::Link(writer, utf8, font)
LocalLink::LocalLink(XRefDefs* defs, XRefDests* dests, PDFWriter* writer, BString* utf8, int32 page)
: ::Link(writer, utf8)
, fDefs(defs)
, fDests(dests)
, fLinkPage(page)
@ -398,7 +403,7 @@ bool LocalLink::Link(XRefDef* def, MatchResult* result) {
if (result->CountResults() >= 2) {
BString label;
result->GetString(1, &label);
if (fDests->Find(def, label.String(), &fDestPage)) {
if (fDests->Find(def, label.String(), &fDestPage, &fDestBounds)) {
result->Start(1);
fContainsLink = true;
fStartPos = result->Start(1) - fUtf8->String();
@ -428,9 +433,11 @@ void LocalLink::DetectLink(int start) {
void LocalLink::CreateLink(float llx, float lly, float urx, float ury) {
char optList[256];
if (fDestPage != fLinkPage) {
BString s(&fUtf8->String()[fStartPos], fEndPos-fStartPos+1);
REPORT(kInfo, fLinkPage, "Link '%s' to page %d", s.String(), fDestPage);
PDF_add_locallink(fWriter->fPdf, llx, lly, urx, ury, fDestPage, "retain");
sprintf(optList, "type=fixed left=%f top=%f", fDestBounds.left, fDestBounds.top);
PDF_add_locallink(fWriter->fPdf, llx, lly, urx, ury, fDestPage, optList);
}
}

View File

@ -168,11 +168,13 @@ class Destination {
private:
BString fLabel;
int32 fPage;
BRect fBoundingBox;
public:
Destination(const char* label, int32 page);
Destination(const char* label, int32 page, BRect bounds);
const char* Label() const { return fLabel.String(); }
int32 Page() const { return fPage; }
BRect BoundingBox() const { return fBoundingBox; }
};
@ -194,8 +196,8 @@ class XRefDests {
public:
XRefDests(int32 n);
bool Add(XRefDef* def, const char* label, int32 page);
bool Find(XRefDef* def, const char* label, int32* page) const;
bool Add(XRefDef* def, const char* label, int32 page, BRect boundingBox);
bool Find(XRefDef* def, const char* label, int32* page, BRect* boundingBox) const;
};
@ -203,10 +205,11 @@ public:
class RecordDests : public XMatchResult {
XRefDests* fDests;
TextLine* fLine;
int32 fPage;
public:
RecordDests(XRefDests* dests, int32 page);
RecordDests(XRefDests* dests, TextLine* line, int32 page);
bool Link(XRefDef* def, MatchResult* result);
bool Dest(XRefDef* def, MatchResult* result);
};
@ -217,15 +220,17 @@ public:
class LocalLink : public Link, XMatchResult {
protected:
XRefDefs* fDefs;
XRefDests* fDests;
XRefDests* fDests;
int32 fLinkPage;
int32 fLinkPage, fDestPage;
int32 fDestPage;
BRect fDestBounds;
void DetectLink(int start);
void CreateLink(float llx, float lly, float urx, float ury);
public:
LocalLink(XRefDefs* defs, XRefDests* dests, PDFWriter* writer, BString* utf8, BFont* font, int32 page);
LocalLink(XRefDefs* defs, XRefDests* dests, PDFWriter* writer, BString* utf8, int32 page);
bool Link(XRefDef* def, MatchResult* result);
bool Dest(XRefDef* def, MatchResult* result);
};