Platforms to show: All Mac Windows Linux Cross-Platform

/DynaPDF/Text Positions


Required plugins for this example: MBS DynaPDF Plugin

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, 19th Jan 2021.

Project "Text Positions.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
EventHandler Sub NewDocument() If WindowCount = 0 Then Dim f As FolderItem = GetOpenFolderItem(FileTypes1.pdf) If f <> Nil Then OpenDocument f End If End If End EventHandler
EventHandler Sub Open() Dim f As FolderItem = FindFile("license.pdf") If f<> Nil And f.Exists Then OpenDocument f End If Dim ff As FolderItem = SpecialFolder.Desktop.child("test.pdf") If ff<> Nil And ff.Exists Then OpenDocument ff End If End EventHandler
EventHandler Sub OpenDocument(item As FolderItem) Dim m As New MainWindow(item) m.show End EventHandler
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 = bbox.top - bbox.Bottom 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 g.TextSize = GetScaleY(m) * t.State.FontSize * factor If t.KerningA <> Nil Then Dim TextPos As Double = 0 For Each k As DynaPDFTextRecordAMBS In t.KerningA // translate native encoding to Unicode Dim decoded As Boolean Dim ktext As String = k.Text TextPos = TextPos - k.Advance Dim TextPosStart As Double = TextPos Dim i As Integer = 0 Dim l As Integer = ktext.LenB Dim TextWidth As Double = 0 while i < l Dim OldTextPos As Double = TextPos TextWidth = 0 dim ProcessedBytes as integer = 0 Dim OneText As String = DynaPDFMBS.TranslateRawCode(t.state.FontHandle, kText, i, TextWidth, 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 TextPos = TextPos + TextWidth // drawing here will not match what DynaPDF draws as we do not take style and spacing in account g.ForeColor = &c00FF00 g.PenWidth = 1 // calculate all 4 points // top left Dim x1 As Double = OldTextPos Dim y1 As Double = 0.0 m.Transform(x1,y1) // bottom left Dim x2 As Double = OldTextPos Dim y2 As Double = t.State.FontSize m.Transform(x2,y2) // bottom right Dim x3 As Double = TextPos Dim y3 As Double = t.State.FontSize m.Transform(x3,y3) // top right Dim x4 As Double = TextPos Dim y4 As Double = 0 m.Transform(x4,y4) If Split Then // draw rectangle (could be rotated or skewed) g.DrawLine x1*factor, (PageHeight-y1)*factor, x2*factor, (PageHeight-y2)*factor g.DrawLine x2*factor, (PageHeight-y2)*factor, x3*factor, (PageHeight-y3)*factor g.DrawLine x3*factor, (PageHeight-y3)*factor, x4*factor, (PageHeight-y4)*factor g.DrawLine x4*factor, (PageHeight-y4)*factor, x1*factor, (PageHeight-y1)*factor End If g.ForeColor = &c0000FF g.TextSize = CalcDistance(x1,y1,x2,y2)*factor Dim y As Double = Min(y1,y2) // on the bottom side we draw text. Dim x As Double = (x3+x1) / 2.0 // middle of the text block for our text Dim tw As Double = g.StringWidth(OneText) g.DrawString OneText, x * factor - tw/2, (PageHeight-y)*factor i = i + ProcessedBytes wend if not split then // box around g.PenWidth = 1 g.ForeColor = &cFF0000 // calculate all 4 points // top left Dim x1 As Double = TextPosStart Dim y1 As Double = 0.0 m.Transform(x1,y1) // bottom left Dim x2 As Double = TextPosStart Dim y2 As Double = t.State.FontSize m.Transform(x2,y2) // bottom right Dim x3 As Double = TextPos Dim y3 As Double = t.State.FontSize m.Transform(x3,y3) // top right Dim x4 As Double = TextPos Dim y4 As Double = 0 m.Transform(x4,y4) g.DrawLine x1*factor, (PageHeight-y1)*factor, x2*factor, (PageHeight-y2)*factor g.DrawLine x2*factor, (PageHeight-y2)*factor, x3*factor, (PageHeight-y3)*factor g.DrawLine x3*factor, (PageHeight-y3)*factor, x4*factor, (PageHeight-y4)*factor g.DrawLine x4*factor, (PageHeight-y4)*factor, x1*factor, (PageHeight-y1)*factor end if next end if next 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 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(self.file, 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
Sub Constructor(file as FolderItem) Self.file = file Self.Title = file.DisplayName // Calling the overridden superclass constructor. Super.Constructor End Sub
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 file As FolderItem
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 If matrix <> Nil Then current.matrix = current.matrix * matrix End If End EventHandler
EventHandler Sub MulMatrix(ObjectPtr as integer, matrix as DynaPDFMatrixMBS) If matrix <> Nil Then current.matrix = current.matrix * matrix Else Break // should not happen End If 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
FileTypes1
Filetype application/pdf
End FileTypes1
End Project

See also:

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


The biggest plugin in space...