Get the designed image size - itext

I learned getting the image size using itextsharp pdf
the image size is the physical dimension before the image was inserted in the pdf.
Dim iwidth As Integer = tg.GetAsNumber(PdfName.WIDTH).IntValue
Dim iheight As Integer = tg.GetAsNumber(PdfName.HEIGHT).IntValue
When I extracted the image is very large, larger than the paper size.
But if you view the image inside the pdf with any pdf reader software, the image is small.
How can I get the designed image size inside the pdf?
Im using this code and I don't know if is the proper, I don't know the next think to do.
Dim pdfDesignedImage As iTextSharp.text.pdf.parser.InlineImageInfo
This is my 2nd account, I was wrong on first. Sorry about that.
I hope I can be better using this account.

There's a poorly named Interface called ITextExtractionStrategy that you can implement that will give you extended information when extracting things from a PDF. I say "poorly named" because although it says "text" it also supports images as well. This interface has 5 methods, 4 of which are text-based and you can just ignore. The method that you are interested in is RenderImage. Below is a full working implementation of this:
Public Class ImageInfoTextExtractionStrategy
Implements iTextSharp.text.pdf.parser.ITextExtractionStrategy
#Region " Extra Methods - Just Ignore "
Public Sub BeginTextBlock() Implements iTextSharp.text.pdf.parser.IRenderListener.BeginTextBlock
End Sub
Public Sub EndTextBlock() Implements iTextSharp.text.pdf.parser.IRenderListener.EndTextBlock
End Sub
Public Sub RenderText(renderInfo As iTextSharp.text.pdf.parser.TextRenderInfo) Implements iTextSharp.text.pdf.parser.IRenderListener.RenderText
End Sub
Public Function GetResultantText() As String Implements iTextSharp.text.pdf.parser.ITextExtractionStrategy.GetResultantText
Return Nothing
End Function
#End Region
''//We'll add all image rectangles to this collection
Private _AllImageRectangles As New List(Of iTextSharp.text.Rectangle)
Public ReadOnly Property AllImageRectangles As List(Of iTextSharp.text.Rectangle)
Get
Return Me._AllImageRectangles
End Get
End Property
Public Sub RenderImage(renderInfo As iTextSharp.text.pdf.parser.ImageRenderInfo) Implements iTextSharp.text.pdf.parser.IRenderListener.RenderImage
''//Get the image's matrix
Dim m = renderInfo.GetImageCTM()
Dim w, h, x, y As Single
''//Get the various parameters from the matrix
w = m(iTextSharp.text.pdf.parser.Matrix.I11)
h = m(iTextSharp.text.pdf.parser.Matrix.I22)
x = m(iTextSharp.text.pdf.parser.Matrix.I31)
y = m(iTextSharp.text.pdf.parser.Matrix.I32)
''//Turn the parameters into a rectangle
Me._AllImageRectangles.Add(New iTextSharp.text.Rectangle(x, y, x + w, y + h))
End Sub
End Class
To use this subclass we pass it to the (once again poorly named) method iTextSharp.text.pdf.parser.PdfTextExtractor.GetTextFromPage(). Normally you'd call this method and assign the string result to a variable but in our case we don't care about the text so don't. To use it you would do this:
''//Path to our pdf with images
Dim PdfWithImage = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "PdfWithImage.pdf")
''//Bind a reader to our PDF
Dim reader As New PdfReader(PdfWithImage)
''//Create an instance of our custom extraction class
Dim strat As New ImageInfoTextExtractionStrategy()
''//Loop through each page in our PDF
For I = 1 To reader.NumberOfPages
''//The GetTextFromPage method does the work even though we are working with images
iTextSharp.text.pdf.parser.PdfTextExtractor.GetTextFromPage(reader, I, strat)
Next
''//Get all image rectangles found
Dim Rects = strat.AllImageRectangles
For Each R In Rects
''//Do something with your rectangles here
Next

Related

Inserting and removing text into a textbox on a SHEET in Libreoffice calc BASIC

As I can't solve my problem I'd like to ask someone more experienced.I created simple dialog (4 fields) to let the user enter few data. After clicking "Submit" button those data should be inserted into textboxes put ON THE SHEET (not on any dialog). How to refer to those sheet texboxes in code to insert those data? Other thing is deleting those data after clicking other button "Clear". Suppose it will be similar to inserting but how this piece of code should look like?
Thanks in advance.
The trick is to create a com.sun.star.drawing.TextShape object and add it to the Draw Page of the target sheet. The following works for me. You should be able to assign it to the appropriate button on your dialog.
Sub InsertTextBox()
Dim oDocument As Object
oDocument = ThisComponent
If oDocument.SupportsService("com.sun.star.sheet.SpreadsheetDocument") Then
Dim sText As String
sText = "Blah,blah, blah!"
Dim oPosition As New com.sun.star.awt.Point
oPosition.X = 1000
oPosition.Y = 1000
Dim oSize As New com.sun.star.awt.Size
oSize.Width = 10000
oSize.Height = 5000
Dim oTextShape As Object
oTextShape = oDocument.createInstance("com.sun.star.drawing.TextShape")
oTextShape.setPosition(oPosition)
oTextShape.setSize(oSize)
oTextShape.setPropertyValue("FillStyle", "SOLID")
oTextShape.Visible = 1
' Give it a name so you can find it again when you want to delete it
oTextShape.setPropertyValue("Name", "Thingy")
Dim oDrawPage As Object
oDrawPage = oDocument.getSheets().getByIndex(0).getDrawPage()
oDrawPage.add(oTextShape)
' Set the string of the text shape AFTER adding it to the
' draw page, otherwise the text will not be set.
oTextShape.setString(sText)
End If
End Sub
In the above routine the TextShape object was given the name "Thingy". You can obviously give it any name you like, but it should be unique. To delete it, you need to loop through all the objects in the draw page, find the one that is a TextShape and has the name you gave it (in this case "Thingy") and remove it. This can be done as follows:
Sub DeleteTextBox()
Dim oDocument As Object
oDocument = ThisComponent
If oDocument.SupportsService("com.sun.star.sheet.SpreadsheetDocument") Then
Dim oDrawPage As Object
oDrawPage = oDocument.getSheets().getByIndex(0).getDrawPage()
Dim oShape As Object
Dim i As Long
For i = (oDrawPage.getCount() - 1) To 0 Step -1
oShape = oDrawPage.getByIndex(i)
If oShape.SupportsService("com.sun.star.drawing.TextShape") Then
If StrComp(oShape.getPropertyValue("Name"), "Thingy") = 0 Then
oDrawPage.remove(oShape)
End If
End If
Next i
End If
End Sub
This will delete all objects of type TextShape and named "Thingy"

Resize and modify selected chart with libreoffice basic

I'm writing a macro with libreoffice basic to modify the styles and size of a selected (and only selected) chart in my spreadsheets.
After many documentation reading and many tries, I managed to have a partial solution to my problem:
Modifying all charts style
I managed to select a chart by it's indexe and modify it's styles with this macro:
Sub ModifyChart
Dim oDoc As Object
Dim oChart As Object
Dim aSize as new com.sun.star.awt.Size
oSheet = ThisComponent.sheets(0)
oCharts = oSheet.getCharts()
oChart = oCharts.getByIndex(0).getEmbeddedObject()
MsgBox oChart.ImplementationName
oChart.Title.String = "My title"
oChart.Title.CharColor = RGB(0,0,200)
oChart.Title.CharFontName = "Arial"
oChart.Title.CharHeight = 16
oChart.SubTitle.String = "My subtitle"
oChart.SubTitle.CharColor = RGB(0,0,200)
oChart.SubTitle.CharFontName = "Arial"
oChart.SubTitle.CharHeight = 12
oChart.Diagram.Wall.FillStyle = com.sun.star.drawing.FillStyle.SOLID
oChart.Diagram.Wall.FillColor = RGB(200,50,150)
oChart.Area.FillStyle = com.sun.star.drawing.FillStyle.SOLID
oChart.Area.FillColor = RGB(100,50,250)
aSize.Width=640
aSize.Height=400
oChart.setsize(aSize) ' Error occur at this point
End Sub
Problem: It's doesn't work with the selected chart and I can't resize it. The method setsize doesn't work for this kind of object. I tryed to get the parent object of the chart ( frame ? or shape ?) without success.
With ImplementationName, I found that the object is a com.sun.star.comp.sc.scshapeobj
Modifying the selected chart size
I managed to modify the selected chart size by using this macro
Sub ResizeChart
Dim oCurrSel As Object
Dim oItem As Object
Dim aSize As New com.sun.star.awt.Size
oCurrSel = ThisComponent.getCurrentSelection()
oItem = oCurrSel.getByIndex(0)
MsgBox oItem.ImplementationName
aSize.width=16000
aSize.height=12000
oItem.setsize(aSize)
End Sub
Problem: I can't access to the other styles of the chart. I tryed to find a method to get the object content without success. I also tryed to investigate the object with oItem.dbg_properties and oItem.dbg_methods but I didn't found anything useful.
With ImplementationName, I found that the object is a com.sun.star.comp.chart2.chartmodel
I have a look to the libreoffice api but I didn't find how these two kind of object are connected.
Is it possible to make what I want with libreoffice basic ?
Does anyone could explain me the hierachical structure of a libreoffice chart object (parents, childs, ...) and how to deal with it ?
Yes, you are absolutely right - parsing the current selection is not a trivial task.
Since the current selection may contain a variety of objects - Cell, CellRange, SheetCellRanges, Shapes, ShapeCollection, GraphicObjectShape and even in some cases just Text, parsing becomes similar to the game "Miner" - each next step requires additional checks (or error handling " blindly "with the help On Error Resume Next)
Your idea of ​​using ImplementationName to identify objects is generally good. But Andrew Pitonyak in 4.1. Debugging And Inspecting Macros wrote "To determine the document type, look at the services it supports ... I assume that this is safer than using getImplementationName()" and I tend to believe him.
The transition from the current selection to the embedded chart can be something like this:
Sub ModifyChartInCurrentSelection
Dim oCurrentSelection As Variant
Dim i As Long
Dim oNextElementOfSelection As Variant
Dim oEmbeddedObject As Variant
Dim oComponent As Variant
Dim aSize As New com.sun.star.awt.Size
oCurrentSelection = ThisComponent.getCurrentSelection()
If oCurrentSelection.supportsService("com.sun.star.drawing.ShapeCollection") Then
For i = 0 To oCurrentSelection.getCount()-1
oNextElementOfSelection = oCurrentSelection.getByIndex(i)
If oNextElementOfSelection.supportsService("com.sun.star.drawing.OLE2Shape") Then
Rem Size of shape (outer wrapper around the chart)
If oNextElementOfSelection.supportsService("com.sun.star.drawing.Shape") Then
aSize = oNextElementOfSelection.getSize()
aSize.Height = aSize.Height * 2 ' or any other
aSize.Width = aSize.Width / 2
oNextElementOfSelection.setSize(aSize)
EndIf
Rem Properties of EmbeddedObject
oEmbeddedObject = oNextElementOfSelection.EmbeddedObject
If Not IsEmpty(oEmbeddedObject) Then
oComponent = oEmbeddedObject.getComponent()
oComponent.getTitle().String = "Foo-bar Foo-bar Foo-bar"
Rem and other settings...
EndIf
EndIf
Next i
EndIf
End Sub

VBA form - Returning a textbox object to be manipulated

I'm creating a game in a vba form. Right now it creates an array 9x9 of textboxes and fills and disables the textboxes with the given information for the game. When creating the textboxes I named them "fieldx-y" so I could look them up easily. I want to somehow put them into an array so that I can look them up like field(x,y) and then do things to them like change the background color of the textbox or change information in it.
Here is the function I wanted to use to find the object using its name and return it to be manipulated.
Public Function getField(x As Integer, y As Integer) As MSForms.TextBox
Dim field As MSForms.TextBox
For Each field In Me.Controls
If Right(field.Name, 1) = y And Left(Right(field.Name, 3), 1) = x Then
getField = field
End If
Next
End Function
And here is how I would like to manipulate it from my userform initialize sub
getField(5,5).Enabled=False
I'm sure I must be doing something very wrong and it's probably because of my lacking understanding of OOP and vba.
Thanks
Since you have chosen a predictable naming convention you can call these controls directly using your naming convention. There is no need to loop through all the controls. Also, I changed your fieldx-y to fieldx_y because - is an illegal character variable names.
Public Function getField(x As Integer, y As Integer) As MSForms.TextBox
set getField = me.controls("field" & x & "_" & y)
End Function
If all you are doing is enabling the control, then you may not actually need to return the textbox, in which case do not add the tb variable to the calling procedure, and change your function to a sub, like:
Public Sub getField(x As Integer, y As Integer)
Dim field As MSForms.TextBox
For Each field In Me.Controls
If Right(field.Name, 1) = y And Left(Right(field.Name, 3), 1) = x Then
'## Disable this textbox
field.Enabled = False
Exit For
End If
Next
End Sub
If you do need to return a textbox to the calling procedure, then do this:
In your calling procedure, you need an object variable to represent the returned MSForms.TextBox:
Dim tb as MSForms.TextBox
Set tb = getField(5,5)
tb.Enabled = False
Then in your function routine, because it is an object, you need the Set keyword:
Set getField = field
Exit For '## You need to escape the loop otherwise it will keep going, giving undesired results.

Binding a 2D array of doubles to a parameter in MS Solver Foundation

How to bind a 2D array to a parameter in Solver Foundation? Have tried defining the array as double(,); as double()() and as a list of tuples(double, i, j).
I have also tried to implement the extension methods to SetBinding, suggested here; http://blogs.msdn.com/b/solverfoundation/archive/2010/06/28/simpler-data-binding-using-linq-and-extension-methods.aspx
Currently fails at third line to bottom; m_cov.SetBinding(CovMatrix), with error "This method is only valid when called on parameters with 0 indexes"
I'm using latest version and working in vb.net. Any help appreciated.
Thanks,
Yug
Public Sub ERC()
Dim m_i = New [Set](Domain.Any, "I")
Dim m_j = New [Set](Domain.Any, "J")
'Dim m_allocation As Decision
Dim CovMatrix As Double()() = {New Double() {0.1, 0.15, 0.4}, New Double() {0.3, 0.5, 0.8}, New Double() {0, 0.33, 0.05}}
Dim m_context As SolverContext = SolverContext.GetContext()
Dim m_model As Model = m_context.CreateModel()
m_model.Name = "ERC"
' Create a Parameter for Cov
Dim m_cov = New Parameter(Domain.Real, "Cov", m_i, m_j)
m_model.AddParameter(m_cov)
' Create a Decision for Allocation
Dim m_allocation As Decision = New Decision(Domain.RealRange(-1.0, 1.0), "Allocation", m_i)
m_model.AddDecision(m_allocation)
' Add Constraint for SumWts
m_model.AddConstraint("SumWts", (Model.Sum(Model.ForEach(m_i, Function(i_1) Model.Abs(Model.Sum(m_allocation(i_1)))))) = 1.0)
' Add Goal for Variance
m_model.AddGoal("Variance", GoalKind.Minimize, Model.Sum(Model.ForEach(m_i, Function(i_2) Model.ForEach(m_j, Function(j_3) Model.Power((Model.Abs(Model.Sum(Model.ForEach(m_j, Function(j_4) Model.Product(m_cov(i_2, j_4), m_allocation(j_4), m_allocation(i_2))))) - Model.Abs(Model.Sum(Model.ForEach(m_j, Function(j_6) Model.Product(m_cov(j_3, j_6), m_allocation(j_6), m_allocation(j_3)))))), 2.0)))))
m_cov.SetBinding(CovMatrix)
m_context.Solve()
Debug.Print(m_allocation.GetValuesByIndex().ToString)
End Sub
The helper class provided by Nathan Brixius makes SetBinding way easier. His helper class is in C#, so I went ahead and converted the particular helper function you will need into VB (see below).
The exception is telling you that the SetBinding function needs to know the indices for the data passed in. MSF is built to handle generic domains, meaning it does not abide by normal array indices. You have to explicitly point out the index information.
The problem with your code is that you are trying to pass in raw arrays without any extra index data. To remedy this on a normal 1D array, you would add indices using KeyValuePair(Of Integer, Double). In this case for a matrix you need a list of Tuple (index1, index2, Double). Essentially, you need to flatten the 3x3 matrix into 9 triples, specifying each value according to a pair of indices.
Here is the VB function to convert your matrix into a list as such:
Private Function ToIEnumerable(Of T)(matrix As IEnumerable(Of IEnumerable(Of T))) As IEnumerable(Of Tuple(Of Integer, Integer, T))
Dim m = matrix.[Select](Function(row, i) row.[Select](Function(cell, j) New Tuple(Of Integer, Integer, T)(i, j, cell)))
Dim cells = From cell In m.SelectMany(Function(c) c)
Return cells
End Function
Include this function in your class, and then change the SetBinding line of code like so:
m_cov.SetBinding(ToIEnumerable(CovMatrix), "Item3", "Item1", "Item2")
Notice the ordering of the Tuple items! By MSF convention, the value field comes before the indices. The same ordering is returned in the Solution output (important to note when you are looking to iterate over the Decisions on the result set).
If you convert the rest of Nathan's helper class, he makes it even easier by overloading the SetBinding function itself to abstract away the ToIEnumerable(data) call as well as the key/value identifier ordering. Then you are able to simply call model.SetBinding(rawMatrix).
Pretty slick, eh? ;)

Stumped trying to implement Save/Load object functionality in a MATLAB UI

I am trying to implement save/load functions in a MATLAB (R2009a) UI. My object implements a layout function that generates a user interface for the object. I am trying to implement the callbacks for the save/load buttons. The save button works and save the object out to a MAT file which can be loaded later.
My problem is implementing the callback for the load button. I cannot seem to get the load to load the data from the MAT file and update the properties of the new object. Any suggestions on where I am going wrong along with suggestions on how I might proceed is greatly appreciated.
The important code is my class definition file of course my actual object implements many more properties and methods but here is a skeleton of what I have
classdef myObj<handle
properties
image % property holds a matlab image matrix
objCount % number of objects in image
end
properties(Transient=true)
parent
children
end
methods
function myObj
% empty constructor
end
function load_object(self)
% ask user for file
[fileName, pathToFile]=uigetfile('*.mat','Select .mat file');
tmp = load(fullfile(pathToFile,fileName);
if isfield(tmp,'obj')
self = tmp.obj;
end
end
LayoutFcn(self) % UI layout function
end
end
The UI layout is defined in a seperate file LayoutFcn.m which basically looks like
function LayoutFcn(self)
% create figure window and add various UI elements
...
% create load button
self.children(end+1) = uipushtool('Parent',hToolbar, ... % set parent to current toolbar
'CData',iconRead('open-document.png'), ... % read icon image from file
'Tag','uiLoad', ...
'ClickedCallback',#(hObj,event)loadingMyObject(self,hObj,event));
% create save button
self.children(end+1) = uipushtool('Parent',hToolbar, ... % set parent to current toolbar
'CData',iconRead('save-document.png'), ... % read icon image from file
'Tag','uiSave', ...
'ClickedCallback',#(hObj,event)savingMyObject(self,hObj,event));
...
end
function loadingMyObject(self,hObj,event)
self.load_object; % call load_object method defined above in class definition
end
function savingMyObject(self,hObj,event)
[fileName,pathName]=uiputfile('.mat','Save object to MAT file');
obj = self;
save(fullfile(pahtName,fileName),'obj')
end
Note: I am using MATLAB R2009a.
The code doesn't throw any errors. The way I wrote the code the parent object (represented by self) does not get updated after the call to LOAD in the method load_object. SO, this has the desired effect:
>> var = myObj;
>> var.load_object;
However, if I use the loadingMyObject callback defined in LayoutFcn.m in this fashion
>> var = myObjl
>> var.LayoutFcn
-> click Load button to call _loadingMyObject_
doesn't affect var properties. That is var will still have its default property values after clicking the Load button.
Changing the load methods to use set as suggested by gnovice throws the following error
??? Error using ==> set
Conversion to double from FujiCalibration is not possible.
even though I have set/get methods for each property; as in
method set.image(self,II)
% ... some data validation code ...
self.image = II
end
Using a loop to set each field as suggested by Mr Fooz is not really an option as my full class has public constant that throw an error when they are set.
I am looking for a solution that would avoid me having to hand code setting each field individually. Although at this point it seems like the only possibility.
I believe Mr Fooz is right. The self variable passed to load_object is an object of type "myObj", but the line:
self = tmp.obj;
is simply overwriting the self variable with the structure stored in tmp.obj. Doing:
self.image = tmp.obj.image;
should instead invoke a set operator for the image property of object self. In the MATLAB documentation there is a sample class definition with a method called "set.OfficeNumber" that illustrates this.
In addition, the following line in your function savingMyObject may be unnecessary:
obj = self;
I think it might make most sense (and make the code a little clearer) if you used the name "obj" in place of the word "self" within your class code (as the documentation tends to do). "self" doesn't appear to be any kind of special keyword in MATLAB (like it may be in other languages). It's just another variable as far as I can tell. =)
EDIT #1:
If the prospect of having to set each property individually in your load_object method doesn't sound like fun, one way around it is if you have a SET method for your object that is designed like the SET method for handle graphics. That SET command can accept a structure input where each field name is a property name and each field value is the new value for that property. Then you would have one call like:
set(self,tmp.obj);
Quite a bit shorter, especially if you have lots of properties to set. Of course, you'd then have to write the new SET method for your object, but the shortened syntax may be worth the extra work if it comes in handy elsewhere too. =)
EDIT #2:
You may be able to use the loop Mr Fooz suggested in conjunction with a try/catch block:
fn = fieldnames(tmp.obj);
for i = 1:numel(fn),
try
self.(fn{i}) = tmp.obj.(fn{i});
catch
% Throw a warning here, or potentially just do nothing.
end
end
Don't assign values to self. All that does is replace the binding to the self variable in the scope of the method call. It does not call a magical copy constructor to replace the object reference in the caller. Instead, copy the fields into self. Try something like:
if isfield(tmp,'obj')
self.image = tmp.obj.image;
self.objCount = tmp.obj.objCount;
end
Combining Mr Fooz's and gnovice's suggestions, I added a SET function with the following definition
function set(self,varargin)
if isa(varargin{1},'FujiCalibration')
tmp = varargin{1};
fns = fieldnames(self);
for i = 1:length(fns)
if strcmpi(fns{i}(1:2),'p_')
self.(fns{i}) = tmp.(fns{i});
end
end
self.calibImage = tmp.calibImage;
else
proplist=fields(self);
for n=1:2:length(varargin)
tmp = proplist(strcmpi(proplist,varargin{n}));
value = varargin{n+1};
switch length(tmp)
case 0
msg = char(strcat('There is no ''', varargin{n}, '''property'));
error('FujiCalibration:setPropertyChk',msg)
case 1
tmp = char(tmp);
self.(tmp) = value;
end
end
end
end
I then modified the load_object method as suggested by gnovice by changing
self = tmp.obj
to
set(self,tmp.obj).
I need to make sure that properties with values I want to persist are prefixed with 'p_'.
Thanks to gnovice and Mr Fooz for their answers.