diff --git a/src/add-ons/print/drivers/pdf/source/Link.cpp b/src/add-ons/print/drivers/pdf/source/Link.cpp index d160ad4168..e32d35cdf9 100644 --- a/src/add-ons/print/drivers/pdf/source/Link.cpp +++ b/src/add-ons/print/drivers/pdf/source/Link.cpp @@ -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(); } diff --git a/src/add-ons/print/drivers/pdf/source/Link.h b/src/add-ons/print/drivers/pdf/source/Link.h index 8444095c68..7b68be0025 100644 --- a/src/add-ons/print/drivers/pdf/source/Link.h +++ b/src/add-ons/print/drivers/pdf/source/Link.h @@ -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); }; diff --git a/src/add-ons/print/drivers/pdf/source/PDFText.cpp b/src/add-ons/print/drivers/pdf/source/PDFText.cpp index d16931f922..5227e739c6 100644 --- a/src/add-ons/print/drivers/pdf/source/PDFText.cpp +++ b/src/add-ons/print/drivers/pdf/source/PDFText.cpp @@ -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); } diff --git a/src/add-ons/print/drivers/pdf/source/XReferences.cpp b/src/add-ons/print/drivers/pdf/source/XReferences.cpp index a799f65617..0e75de994d 100644 --- a/src/add-ons/print/drivers/pdf/source/XReferences.cpp +++ b/src/add-ons/print/drivers/pdf/source/XReferences.cpp @@ -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); } } diff --git a/src/add-ons/print/drivers/pdf/source/XReferences.h b/src/add-ons/print/drivers/pdf/source/XReferences.h index 7d72b72a9a..4e3e919cd0 100644 --- a/src/add-ons/print/drivers/pdf/source/XReferences.h +++ b/src/add-ons/print/drivers/pdf/source/XReferences.h @@ -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); };