Platforms to show: All Mac Windows Linux Cross-Platform
/DynaPDF/PDF Viewer Example with links
Required plugins for this example: MBS MacCG Plugin, MBS Picture Plugin, MBS DynaPDF Plugin, MBS MacCF 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.xojo_binary_project"
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:
The items on this page are in the following plugins: MBS DynaPDF Plugin.