Xojo Developer Conference
25/27th April 2018 in Denver.
MBS Xojo Conference
6/7th September 2018 in Munich, Germany.

Platforms to show: All Mac Windows Linux Cross-Platform

/DynaPDF/Text Positions
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /DynaPDF/Text Positions
This example is the version from Wed, 20th Feb 2018.
Project "Text Positions.rbp"
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
End Class
Class MainWindow Inherits Window
Control CheckSplit Inherits CheckBox
ControlInstance CheckSplit Inherits CheckBox
EventHandler Sub Action() split = me.Value canvas1.Invalidate End EventHandler
End Control
Control Canvas1 Inherits Canvas
ControlInstance Canvas1 Inherits Canvas
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect) const factor = 2.0 dim lines() as string dim Page as DynaPDFPageMBS = pdf.GetPage(1) dim bbox as DynaPDFRectMBS = page.BBox(page.kpbMediaBox) dim PageWidth as integer = bbox.Width dim PageHeight as integer = abs(bbox.Bottom - bbox.top) if ShowPDF then // we draw pdf as background dim pic as Picture = pdf.RenderPagePicture(1, PageWidth*factor, PageHeight*factor, 2, nil) g.DrawPicture pic, 0, 0 end if // now draw text blocks for each t as PDFText in texts dim m as DynaPDFMatrixMBS = t.EffectiveMatrix //XY Axis dim xAxis As Double = GetScaleX(m) dim yAxis As Double = GetScaleY(m) //font infomation dim fontinfo As DynaPDFFontInfoMBS = t.State.FontInfo dim ascent As Single = fontinfo.Ascent dim descent As Single = abs(fontinfo.Descent) //font size dim fontSize as Double = t.State.FontSize * yAxis dim x1 as Double = 0 dim y1 as Double = 0 dim x2 as Double = 0 dim y2 as Double = t.State.FontSize dim fx as Double = 0 dim fy as Double = t.State.FontSize 'dim MM as DynaPDFMatrixMBS = t.Matrix * t.State.matrix 'dim ffx as Double = fx * M.b + fy * M.d '+ M.y dim ffy as Double = fx * M.b + fy * M.d '+ M.y g.TextSize = ffy * factor dim xx1,yy1,xx2,yy2 as Double if t.KerningA <> nil then for each k as DynaPDFTextRecordAMBS in t.KerningA // translate native encoding to Unicode dim decoded as Boolean dim ktext as string = k.Text x1 = x1 - k.Advance x2 = x2 - k.Advance xx1 = x1 * M.a + y1 * M.c + M.x yy1 = x1 * M.b + y1 * M.d + M.y xx2 = x2 * M.a + y2 * M.c + M.x yy2 = x2 * M.b + y2 * M.d + M.y // swap coordinates from bottom-up in PDF to top-down in Xojo yy1 = PageHeight - yy1 yy2 = PageHeight - yy2 // safe position dim xxx1 as Double = xx1 dim i as integer = 0 dim l as integer = ktext.lenb dim width as Double = 0 while i < l width = 0 dim ProcessedBytes as integer = 0 dim OneText as string = DynaPDFMBS.TranslateRawCode(t.state.FontHandle, kText, i, width, decoded, t.state.CharSpacing, t.state.WordSpacing, t.State.TextScale, ProcessedBytes) lines.append OneText+": "+str(width) if width <= 0.0 then Break // something broken? end if if ProcessedBytes <= 0 then break // should not happen ProcessedBytes = 1 end if // drawing here will not match what DynaPDF draws as we do not take style and spacing in account g.ForeColor = &c0000FF g.DrawString OneText, xx1*factor, (yy1)*factor if split then g.ForeColor = &c00FF00 g.PenWidth=1 g.DrawRect xx1*factor,yy2*factor,(width*xAxis)*factor,abs(yy2-yy1)*factor end if // advance position xx1 = (xx1 + width*xAxis) x1 = (x1 + width) // next i = i + ProcessedBytes wend if not split then // box around g.PenWidth = 1 g.ForeColor = &cFF0000 g.DrawRect xxx1*factor,yy2*factor,abs(xx1-xxx1)*factor,abs(yy2-yy1)*factor end if next end if next call pdf.EndPage End EventHandler
End Control
Control CheckShowPDF Inherits CheckBox
ControlInstance CheckShowPDF Inherits CheckBox
EventHandler Sub Action() ShowPDF = me.Value canvas1.Invalidate End EventHandler
End Control
EventHandler Sub Open() dim f as FolderItem = FindFile("license.pdf") if f=nil or not f.Exists then Return end if dim p as new MyDynapdfMBS p.SetLicenseKey "Pro" // For this example you can use a Pro or Enterprise License call p.CreateNewPDF(Nil) // Skip anything that is not required call p.SetImportFlags p.kifImportAll+p.kifImportAsPage // From which PDF file do you want to extract the images? call p.OpenImportFile(f, p.kptOpen, "") // Comment this out if you want to extract the images from specific pages only call p.ImportPDFFile(1, 1.0, 1.0) call p.CloseImportFile dim parser as new MyDynaPDFParseInterfaceMBS(p) 'If you want to create a multipage TIFF then create the output image here 'and call AddImage() only in the callback function. After the loop 'returns call CloseImage() to close the image file. call p.EditPage 1 call p.ParseContent(parser, p.kpfNone) call p.EndPage self.texts = parser.Texts self.pdf = p End EventHandler
Private Function CalcDistance(x1 as double, y1 as Double, x2 as Double, y2 as Double) As Double // Distance between two points dim dx as double = x2-x1 dim dy as double = y2-y1 return sqrt(dx*dx + dy*dy) End Function
Private Function GetScaleX(m As DynapdfMatrixMBS) As Double Dim x1 As double= 0.0 Dim y1 As double= 0.0 Dim x2 As double= 1.0 Dim y2 As double= 0.0 Transform(m, x1, y1) Transform(m, x2, y2) if (y1 > y2) then return -CalcDistance(x1, y1, x2, y2) else return CalcDistance(x1, y1, x2, y2) end if End Function
Private Function GetScaleY(m As DynapdfMatrixMBS) As Double Dim x1 As double= 0.0 Dim y1 As double= 0.0 Dim x2 As double= 0.0 Dim y2 As double= 1.0 Transform(m, x1, y1) Transform(m, x2, y2) if (y1 > y2) then return -CalcDistance(x1, y1, x2, y2) else return CalcDistance(x1, y1, x2, y2) end if End Function
Property ShowPDF As Boolean = true
Property pdf As MyDynaPDFMBS
Property split As Boolean
Property texts() As PDFText
End Class
MenuBar MainMenuBar
MenuItem FileMenu = "&File"
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 PDFState
Sub Constructor(other as PDFState) self.CharSpacing = other.CharSpacing self.ExtGState = other.ExtGState self.FontHandle = other.FontHandle self.FontInfo = other.FontInfo self.FontName = other.FontName self.FontSize = other.FontSize self.FontStyle = other.FontStyle self.MiterLimit = other.MiterLimit self.textDrawMode = other.textDrawMode self.TextScale = other.TextScale self.WordSpacing = other.WordSpacing self.matrix = new DynaPDFMatrixMBS(other.matrix) // copy End Sub
Sub constructor() matrix = new DynaPDFMatrixMBS // makes new identity matrix FontSize = 1.0 CharSpacing = 0.0 TextDrawMode = DynaPDFMBS.kdmNormal TextScale = 100.0 WordSpacing = 0.0 End Sub
Property CharSpacing As Double
Property ExtGState As DynaPDFExtGState2MBS
Property FontHandle As Integer
Property FontInfo As DynaPDFFontInfoMBS
Property FontName As String
Property FontSize As double
Property FontStyle As Integer
Property Leading As Double
Property MiterLimit As double
Property TextScale As Double
Property WordSpacing As Double
Property matrix As DynaPDFMatrixMBS
Property textDrawMode As Integer
End Class
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 // 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
Property IgnoreWarnings As Boolean
End Class
Class MyDynaPDFParseInterfaceMBS Inherits DynaPDFParseInterfaceMBS
EventHandler Function BeginTemplate(ObjectPtr as integer, Handle as integer, BBox as DynaPDFRectMBS, Matrix as DynaPDFMatrixMBS) As integer current.matrix = current.matrix * matrix End EventHandler
EventHandler Sub MulMatrix(ObjectPtr as integer, matrix as DynaPDFMatrixMBS) current.matrix = current.matrix * matrix End EventHandler
EventHandler Function RestoreGraphicState() As integer current = states.pop End EventHandler
EventHandler Function SaveGraphicState() As integer states.Append current current = new PDFState(Current) End EventHandler
EventHandler Sub SetCharSpacing(ObjectPtr as integer, Value as double) current.CharSpacing = value End EventHandler
EventHandler Sub SetExtGState(ObjectPtr as integer, GS as DynaPDFExtGState2MBS) current.ExtGState = gs End EventHandler
EventHandler Sub SetFont(ObjectPtr as integer, fontType as integer, Embedded as boolean, FontName as string, Style as integer, FontSize as double, FontHandle as integer, FontInfo as DynaPDFFontInfoMBS) current.FontName = FontName current.FontStyle = Style current.FontSize = FontSize current.FontInfo = FontInfo current.FontHandle = FontHandle End EventHandler
EventHandler Sub SetLeading(ObjectPtr as integer, Value as double) current.Leading = value End EventHandler
EventHandler Sub SetMiterLimit(ObjectPtr as integer, Value as double) current.MiterLimit = value End EventHandler
EventHandler Sub SetTextDrawMode(ObjectPtr as integer, Mode as integer) current.textDrawMode = mode End EventHandler
EventHandler Sub SetTextScale(ObjectPtr as integer, Value as double) current.TextScale = value End EventHandler
EventHandler Sub SetWordSpacing(ObjectPtr as integer, Value as double) current.WordSpacing = value End EventHandler
EventHandler Function ShowTextArrayA(ObjectPtr as integer, Matrix as DynaPDFMatrixMBS, Kerning() as DynaPDFTextRecordAMBS, Count as integer, Width as double) As integer dim t as new PDFText t.State = new PDFState(current) t.Count = Count t.Width = Width t.KerningA = Kerning t.Matrix = matrix texts.Append t End EventHandler
Sub Constructor(p as MyDynaPDFMBS) self.current = new PDFState self.pdf = p End Sub
Property States() As PDFState
Property Texts() As PDFText
Property current As PDFState
Property pdf As dynaPDFmbs
End Class
Module UtilModule
Function FindFile(name as string) As FolderItem // Look for file in parent folders from executable on dim parent as FolderItem = app.ExecutableFile.Parent while parent<>Nil dim file as FolderItem = parent.Child(name) if file<>Nil and file.Exists then Return file end if parent = parent.Parent wend End Function
Sub Transform(m as DynaPDFMatrixMBS, byref x as Double, byref y as double) dim tx as double = x x = tx * M.a + y * M.c + M.x y = tx * M.b + y * M.d + M.y End Sub
End Module
Class PDFText
Function EffectiveMatrix() As DynaPDFMatrixMBS return matrix * state.matrix End Function
Property Count As Integer
Property Decoded As boolean
Property KerningA() As DynapdfTextRecordAMBS
Property Matrix As DynapdfMatrixMBS
Property Source() As DynapdfTextRecordAMBS
Property State As PDFState
Property Width As Double
End Class
End Project

See also:

Feedback, Comments & Corrections

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

MBS Xojo Chart Plugins