Xojo Conferences
XDCMay2019MiamiUSA

Platforms to show: All Mac Windows Linux Cross-Platform

/DynaPDF/PDF Viewer Example with links
Function:
Required plugins for this example: MBS DynaPDF Plugin, MBS MacCG Plugin, MBS Picture Plugin
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /DynaPDF/PDF Viewer Example with links
This example is the version from Sat, 20th Oct 2017.
Project "PDF Viewer Example with links.rbp"
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
EventHandler Sub NewDocument() dim f as FolderItem = GetOpenFolderItem(FileTypes1.Pdf) if f<>Nil then OpenDocument f end if End EventHandler
EventHandler Sub OpenDocument(item As FolderItem) PDFPreview.RunShared item End EventHandler
End Class
MenuBar MainMenuBar
MenuItem FileMenu = "&File"
MenuItem FileClose = "Close"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Edit"
MenuItem EditUndo = "&Undo"
MenuItem EditSeparator1 = "-"
MenuItem EditCut = "Cu&t"
MenuItem EditCopy = "&Copy"
MenuItem EditPaste = "&Paste"
MenuItem EditClear = "#App.kEditClear"
MenuItem EditSeparator2 = "-"
MenuItem EditSelectAll = "Select &All"
End MenuBar
Class PDFPreview Inherits Window
Const StepSize = 5
Control Out Inherits Canvas
ControlInstance Out Inherits Canvas
EventHandler Function KeyDown(Key As String) As Boolean Select case asc(key) case 30 MovePrev Return true case 31 MoveNext Return true end Select End EventHandler
EventHandler Function MouseDown(X As Integer, Y As Integer) As Boolean if zoomed then me.MouseCursor = System.Cursors.HandClosed mx = x my = y LastClick = ticks isMoving = true else zoomed = true isMoving = false DrawOffsetX = 0 DrawOffsetY = 0 dim faktor as Double = min( me.Height / Pic.Height, me.Width / Pic.Width) if faktor>1 then faktor = 1 end if dim mx as integer = x dim my as integer = y // Calculate new size dim pw as integer = Pic.Width * faktor dim ph as integer = Pic.Height * faktor dim px as integer = (me.Width - pw)/2 dim py as integer = (me.height - ph)/2 x = x - px y = y - py x = x / faktor y = y / faktor x = mx - x y = my - y move x, y me.Invalidate(False) end if Return true End EventHandler
EventHandler Sub MouseDrag(X As Integer, Y As Integer) if isMoving then dim xx as integer = x-mx dim yy as integer = y-my move xx, yy mx = x my = Y end if End EventHandler
EventHandler Sub MouseEnter() if zoomed then 'me.MouseCursor = System.Cursors.HandOpen me.MouseCursor = System.Cursors.MagnifySmaller else me.MouseCursor = System.Cursors.MagnifyLarger end if End EventHandler
EventHandler Sub MouseExit() me.MouseCursor = System.Cursors.StandardPointer End EventHandler
EventHandler Sub MouseUp(X As Integer, Y As Integer) me.MouseCursor = System.Cursors.MagnifySmaller isMoving = false if abs(ticks-LastClick) < 30 then // 0,5 sekunden zoomed = false me.Invalidate(false) me.MouseCursor = System.Cursors.MagnifyLarger end if End EventHandler
EventHandler Function MouseWheel(X As Integer, Y As Integer, deltaX as Integer, deltaY as Integer) As Boolean if zoomed then move -deltax, -deltay else static time as integer if abs(ticks-time)>30 then time = ticks // nur alle 0,5 Sekunden if deltay > 0 then MoveNext elseif deltay< 0 then MovePrev end if end if end if Return true End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect) if pic = nil then g.clearRect 0, 0, g.width, g.height elseif zoomed then g.DrawPicture Pic, DrawOffsetX, DrawOffsetY, pic.Width, pic.Height, 0, 0, pic.Width, pic.Height else g.clearRect 0, 0, g.width, g.height // Calculate scale factor dim faktor as Double = min( g.Height / Pic.Height, g.Width / Pic.Width) if faktor>1 then faktor = 1 end if // Calculate new size dim w as integer = Pic.Width * faktor dim h as integer = Pic.Height * faktor dim x as integer = (g.Width - w)/2 dim y as integer = (g.height - h)/2 g.DrawPicture Pic, x, y, w, h, 0, 0, Pic.Width, Pic.Height end if End EventHandler
End Control
Control SaveButton Inherits PushButton
ControlInstance SaveButton Inherits PushButton
EventHandler Sub Action() dim f as FolderItem = GetSaveFolderItem(FileTypes1.Pdf, "output.pdf") if f = nil then Return dim b as BinaryStream = BinaryStream.Create(f, true) b.Write PDFData b.Close Exception io as IOException MsgBox "Failed to write file." End EventHandler
End Control
Control PrintButton Inherits PushButton
ControlInstance PrintButton Inherits PushButton
EventHandler Sub Action() #if TargetMachO then PrintMac true #elseif TargetWin32 then dim r as new RegistryItem("HKEY_CLASSES_ROOT\Software\Adobe\Acrobat\Exe") dim s as string = r.DefaultValue if len(s)=0 then Break MsgBox "Failed to find Acrobat Reader." else static sh as shell sh = new shell sh.Mode=2 sh.Execute s+" /p "+PDFFile.ShellPath 'sh.Execute s+" /h /p "+f.ShellPath end if #else Break #endif End EventHandler
End Control
Control MyCloseButton Inherits PushButton
ControlInstance MyCloseButton Inherits PushButton
EventHandler Sub Action() close End EventHandler
End Control
Control List Inherits Listbox
ControlInstance List Inherits Listbox
EventHandler Function CellBackgroundPaint(g As Graphics, row As Integer, column As Integer) As Boolean if row < me.ListCount then dim LineV as Variant = me.CellTag(row,0) dim PageIndex as integer = me.RowTag(row) // selected? if me.ListIndex >= 0 then dim SelectedPageIndex as integer = me.RowTag(me.ListIndex) if SelectedPageIndex = PageIndex then g.ForeColor = HighlightColor g.FillRect 0, 0, g.Width, g.Height else g.ForeColor = &cFFFFFF g.FillRect 0, 0, g.Width, g.Height end if end if // draw page if linev<>Nil then dim p as Picture = pages(PageIndex) if p<>Nil then dim w as integer = p.Width dim xx as integer = (g.Width -w)/2 g.DrawPicture p, xx, 0, w, StepSize, 0, LineV.IntegerValue, w, StepSize end if end if Return true end if End EventHandler
EventHandler Sub Change() if me.ListIndex >= 0 then dim v as Variant = me.RowTag(me.ListIndex) if v <> nil then //RenderPage v+1 RenderPageWithLinks v+1 DrawOffsetX = 0 DrawOffsetY = 0 out.Invalidate(False) end if // fix redrawing issues for i as integer = 0 to me.ListCount-1 me.InvalidateCell(i,0) next end if End EventHandler
EventHandler Function KeyDown(Key As String) As Boolean Select case asc(key) case 30 MovePrev Return true case 31 MoveNext Return true end Select End EventHandler
End Control
Control Thread1 Inherits Thread
ControlInstance Thread1 Inherits Thread
EventHandler Sub Run() // render page previews dim pdf as new MyDynaPDFMBS if not pdf.CreateNewPDF(nil) then // create pdf in memory Return end if dim e as integer if PDFFile = nil then e = pdf.OpenImportBuffer(PDFData) // load from memory else e = pdf.OpenImportFile(PDFFile) // load from file end if if e<>0 then // failed Return end if call pdf.SetImportFlags(pdf.kifImportAsPage) // important! Import as page makes the rendering faster. e = pdf.ImportPDFFile(1,1.0,1.0) if e < 0 then Return end if dim c as integer = pdf.GetPageCount for i as integer = 1 to c me.Sleep(10) if list= nil then Return // Fenster schon zu dim page as DynaPDFPageMBS = pdf.GetPage(i) dim w as integer = 148 dim h as integer = 198 dim pic as Picture = pdf.RenderPagePicture(i, w, h, pdf.kpsFitBest) newpages.Append pic next End EventHandler
End Control
Control UpdateTimer Inherits Timer
ControlInstance UpdateTimer Inherits Timer
EventHandler Sub Action() while UBound(NewPages)>=0 dim p as Picture = NewPages(0) NewPages.Remove 0 pages.Append p dim v as Variant = UBound(pages) List.AddRow "" List.RowTag(List.LastIndex) = v dim h as integer = p.Height-1 for i as integer = 0 to h step StepSize List.AddRow "" List.RowTag(List.LastIndex) = v List.CellTag(List.LastIndex,0) = i next List.AddRow "" List.RowTag(List.LastIndex) = v wend End EventHandler
End Control
Control RotateButton Inherits PushButton
ControlInstance RotateButton Inherits PushButton
EventHandler Sub Action() pic = pic.Rotate270MBS move 0, 0 End EventHandler
End Control
Control ZoomButton Inherits PushButton
ControlInstance ZoomButton Inherits PushButton
EventHandler Sub Action() zoomed = not zoomed out.Invalidate(False) End EventHandler
End Control
Control Wheel Inherits ProgressWheel
ControlInstance Wheel Inherits ProgressWheel
EventHandler Sub Open() me.Left = (Width -me.Width )/2 me.top = (Height-me.Height)/2 End EventHandler
End Control
EventHandler Function KeyDown(Key As String) As Boolean Select case asc(key) case 30 MovePrev Return true case 31 MoveNext Return true end Select End EventHandler
EventHandler Sub Open() list.DefaultRowHeight = StepSize End EventHandler
EventHandler Sub Resized() move 0,0 End EventHandler
EventHandler Sub Resizing() move 0,0 End EventHandler
Function FileClose() As Boolean close Return True End Function
Sub CheckFileFormat() if PDFData <> "" then if leftb(PDFData,3) = "PNG" then isBild = true Return end if if leftb(PDFData,4) = "%PDF" then isPDF = true Return end if Return end if dim b as BinaryStream = BinaryStream.Open(PDFFile) dim s as string = b.Read(4) if leftb(s,3) = "PNG" then isBild = true Return end if if leftb(s,4) = "%PDF" then isPDF = true Return end if Exception io as IOException // no file to read? End Sub
Sub HideWheel() wheel.visible = false out.Visible = true UpdateTimer.Enabled = true list.Visible = out.Left > 0 MyCloseButton.Enabled = true PrintButton.Enabled = true RotateButton.Enabled = true SaveButton.Enabled = true ZoomButton.Enabled = true if out.MouseX >= 0 and out.MouseX <= out.Width then if out.MouseY >= 0 and out.MouseY <= out.Height then me.MouseCursor = System.Cursors.MagnifyLarger end if end if End Sub
Sub MoveNext() if list.ListIndex < 0 then list.ListIndex = 0 else dim SelectedPageIndex as integer = list.RowTag(list.ListIndex) dim SuchePageIndex as integer = SelectedPageIndex + 1 for i as integer = list.ListIndex+1 to List.ListCount-1 dim pageIndex as integer = List.RowTag(i) if pageIndex = SuchePageIndex then List.ListIndex = i exit end if next end if End Sub
Sub MovePrev() if list.ListIndex < 0 then list.ListIndex = 0 else dim SelectedPageIndex as integer = list.RowTag(list.ListIndex) dim SuchePageIndex as integer = SelectedPageIndex - 1 for i as integer = list.ListIndex-1 downto 0 dim pageIndex as integer = List.RowTag(i) if pageIndex = SuchePageIndex then List.ListIndex = i exit end if next end if End Sub
Sub PrintMac(ShowDialog as Boolean) #if TargetMacOS then // Print PDF on Mac dim p as CGPDFDocumentMBS = CGPDFDocumentMBS.CreateWithData(PDFData) if p=nil then MsgBox "Failed to read the PDF." Return end if dim g as Graphics if ShowDialog then g = OpenPrinterDialog else g = OpenPrinter end if if g<>Nil then dim c as integer=p.PageCount for i as integer=1 to c g.DrawCGPDFDocumentMBS p, p.CropBox(i), i if i<c then g.NextPage end if next end if #endif End Sub
Shared Function ReadFile(file as FolderItem) As string dim b as BinaryStream = BinaryStream.Open(file) Return b.Read(B.Length) Exception io as IOException Break End Function
Sub RenderPage(n as integer) dim pdf as MyDynaPDFMBS = MainThreadPDF if pdf<>Nil then dim page as DynaPDFPageMBS = pdf.GetPage(n) dim mr as DynaPDFRectMBS = page.BBox(page.kpbMediaBox) dim cr as DynaPDFRectMBS = page.BBox(page.kpbCropBox) if cr<>Nil then mr = cr end if dim w as integer = mr.Width dim h as integer = abs(mr.Bottom-mr.top) w = W * 2 h = h * 2 pic = pdf.RenderPagePicture(n, w, h, pdf.kpsFitBest) else pic = nil end if zoomed = false End Sub
Sub RenderPageWithLinks(n as Integer) // this is modified version of RenderPage. // we use a DynaPDFRasterizerMBS for more options and draw for all web links a rectangle and the URL in red. dim pdf as MyDynaPDFMBS = MainThreadPDF dim page as DynaPDFPageMBS = pdf.GetPage(n) dim mr as DynaPDFRectMBS = page.BBox(page.kpbMediaBox) dim cr as DynaPDFRectMBS = page.BBox(page.kpbCropBox) if cr<>Nil then mr = cr end if dim w as integer = mr.Width dim h as integer = abs(mr.Bottom-mr.top) w = W * 2 h = h * 2 dim t as integer = 0 dim o as new DynaPDFRasterImageMBS o.InitWhite = true // Rotation o.DefScale = o.kpsFitBest o.Flags = o.krfDefault dim r as new DynaPDFRasterizerMBS(pdf, w, h) //dim p as DynaPDFPageMBS = pdf.GetPage(page) dim p as DynaPDFPageMBS = pdf.GetPage(n) if r.RenderPage(p, o) then pic = r.Pic dim PageSpace as DynapdfMatrixMBS = o.PageSpace // now show rectangles around links if pdf <> NIL and pic<>Nil then dim g as Graphics = pic.Graphics dim u as integer = pdf.GetAnnotCount-1 dim links, rechts, oben, unten as Double 'msgbox "RenderPageWithLinks : In if pdf <> NIL and pic <> NIL construct + pdf.GetAnnotCount-1 = " + str(u) + " and page number n = " + str(n) for i as integer = 0 to u 'msgbox "RenderPageWithLinks : i = " + str(i) dim b as DynaPDFAnnotationExMBS = pdf.GetAnnotEx(i) //if b.PageNum<>page then Continue if b.PageNum<>n then Continue dim bbox as DynaPDFRectMBS = b.BBox if b.Type = pdf.katWebLink then links = bbox.Left rechts = bbox.Right oben = bbox.Top unten = bbox.Bottom PageSpace.Transform(links, oben) PageSpace.Transform(rechts, unten) dim URL as string = b.DestFile dim ww as integer = rechts-links dim hh as integer = unten-oben if ww<0 then links = links + ww ww = -ww end if if hh<0 then oben = oben + hh hh = -hh end if g.ForeColor = &cFF0000 g.DrawRect links, oben, ww, hh g.DrawString URL, links, oben t = t + 1 end if next end else // failed break pic = nil end if End Sub
Function RenderPreview() As Boolean CheckFileFormat if list = nil then Return false // Fenster schon zu if isPDF then dim pdf as new MyDynaPDFMBS // load CharacterMaps if you want to correctly process asian fonts 'call pdf.SetCMapDir(SpecialFolder.Desktop.Child("CMap"), pdf.klcmRecursive) MainThreadPDF = pdf if not pdf.CreateNewPDF(nil) then // create pdf in memory Return false end if dim e as integer if PDFFile = nil then e = pdf.OpenImportBuffer(PDFData) // load from memory else e = pdf.OpenImportFile(PDFFile) // load from file end if if e<>0 then // failed Return false end if call pdf.SetImportFlags(pdf.kifImportAsPage + pdf.kifImportAll) // important! Import as page makes the rendering faster. e = pdf.ImportPDFFile(1,1.0,1.0) if e < 0 then Return false end if dim c as integer = pdf.GetPageCount RenderPageWithLinks 1 if c = 1 then if list= nil then Return false // Fenster schon zu list.Visible = false out.left = 0 out.Width = self.Width else Thread1.run end if Return true else pic = Picture.Open(PDFFile) if pic <> nil then Return true end if end if End Function
Sub Run(file as FolderItem) self.PDFData = ReadFile(file) self.PDFFile = file if self.RenderPreview then self.show self.HideWheel else self.close end if End Sub
Sub Run(PDFData as string) self.PDFData = PDFData if RenderPreview then self.HideWheel self.show else self.close end if End Sub
Shared Sub RunShared(file as FolderItem) dim p as new PDFPreview p.run file End Sub
Shared Sub RunShared(PDFData as string) dim p as new PDFPreview p.run PDFData End Sub
Sub move(deltax as integer, deltay as integer) DrawOffsetX = DrawOffsetX + deltax DrawOffsetY = DrawOffsetY + deltay if DrawOffsetX>0 then DrawOffsetX = 0 end if if DrawOffsetY>0 then DrawOffsetY = 0 end if if pic <> nil then dim w as integer = out.Width - pic.Width dim h as integer = out.Height - pic.Height if DrawOffsetX < w then DrawOffsetX = w end if if DrawOffsetY < h then DrawOffsetY = h end if end if out.Invalidate(False) End Sub
Property DrawOffsetX As Integer
Property DrawOffsetY As Integer
Property LastClick As Integer
Property MainThreadPDF As MyDynaPDFMBS
Property NewPages() As Picture
Property PDFData As string
Property PDFFile As FolderItem
Property Pic As Picture
Property isBild As Boolean
Property isMoving As Boolean
Property isPDF As Boolean
Property mx As Integer
Property my As Integer
Property pages() As picture
Property zoomed As Boolean
End Class
FileTypes1
Filetype application/pdf
End FileTypes1
Class MyDynaPDFMBS Inherits DynaPDFMBS
EventHandler Function Error(ErrorCode as integer, ErrorMessage as string, ErrorType as integer) As integer // output all messages on the console: System.DebugLog str(ErrorCode)+": "+ErrorMessage if ErrorCode = 116 then // WriteFText cancelled Return 0 end if if ErrorCode = 63 then // Type1 Font nicht unterstützt Return 0 end if if app.CurrentThread <> nil then Break Return 0 // ignore in threads end if // and display dialog: Dim d as New MessageDialog //declare the MessageDialog object Dim b as MessageDialogButton //for handling the result d.icon=MessageDialog.GraphicCaution //display warning icon d.ActionButton.Caption="Continue" d.CancelButton.Visible=True //show the Cancel button // a warning or an error? if BitAnd(ErrorType, me.kE_WARNING) = me.kE_WARNING then // if user decided to ignore, we'll ignore if IgnoreWarnings then Return 0 d.Message="A warning occurred while processing your PDF code." // we add a third button to display all warnings d.AlternateActionButton.Caption = "Ignore warnings" d.AlternateActionButton.Visible = true else d.Message="An error occurred while processing your PDF code." end if d.Explanation = str(ErrorCode)+": "+ErrorMessage b=d.ShowModal //display the dialog Select Case b //determine which button was pressed. Case d.ActionButton Return 0 // ignore Case d.AlternateActionButton IgnoreWarnings = true Return 0 // ignore Case d.CancelButton Return -1 // stop End select End EventHandler
EventHandler Function PageBreak(LastPosX as double, LastPosY as double, PageBreak as boolean) As integer Return -1 End EventHandler
Property IgnoreWarnings As Boolean
End Class
End Project

See also:

Feedback, Comments & Corrections

The items on this page are in the following plugins: MBS DynaPDF Plugin.




Links
MBS Xojo Plugins