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:
parent
61594003dd
commit
2e07400817
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user