I am using the iTextSharp assembly version 5.5.10 and think to face a bug. When I position my SetSimpleColumns for some unknown reason the 11th till the 14t column remain on the same line.
I first thought it was related to margins, but my rectangles with the same coordinates position correct.
This is how the result looks like:
The code is as follow:
Dim iADsPerPage As Integer = 14
Dim iRow As Integer = 0
dBottom = 760 'Next Line (one line = 15)
For Each oRow As DataRow In dtADs.Rows
iRow = iRow + 1
dBottom = dBottom - 43.6 'Next Line (one line = 15)
myText = New Phrase(oRow("BulletinReference").ToString, oFont)
dLeftSide = 102.0
dCellSize = 106.0
ct.SetSimpleColumn(myText, dLeftSide, dBottom + dFontLine, dLeftSide + dCellSize, dCellSize, 0, Element.ALIGN_CENTER)
ct.Go()
myText = New Phrase(Left(oRow("BulletinReference").ToString, 40), oFont)
dLeftSide = 210.0
dCellSize = 302.0
cb.Rectangle(dLeftSide, dBottom, dCellSize, dCellHeight)
cb.Stroke()
Response.Write(">" & " " & dLeftSide & " " & dBottom + dFontLine & " " & dLeftSide + dCellSize & " " & dCellSize & "<<br>")
ct.SetSimpleColumn(myText, dLeftSide, dBottom + dFontLine, dLeftSide + dCellSize, dCellSize, 0, Element.ALIGN_LEFT)
ct.Go()
If iRow = 11 Then 'force position as test
myText = New Phrase("hello world!!!!!!!!!!!!!!!!!", oFont)
ct.SetSimpleColumn(myText, 210, 100, 210 + 302, 302, 0, Element.ALIGN_LEFT)
ct.Go()
End If
Next
You use the wrong value in the fifth argument of SetSimpleColumn (and also in the third but that is not quite as wrong):
ct.SetSimpleColumn(myText, dLeftSide, dBottom + dFontLine, dLeftSide + dCellSize, dCellSize, 0, Element.ALIGN_CENTER)
As you see you always use dCellSize there, probably assuming that argument to be the height of the area, but the method is defined as:
/**
* Simplified method for rectangular columns.
* #param phrase a <CODE>Phrase</CODE>
* #param llx the lower left x corner
* #param lly the lower left y corner
* #param urx the upper right x corner
* #param ury the upper right y corner
* #param leading the leading
* #param alignment the column alignment
*/
virtual public void SetSimpleColumn(Phrase phrase, float llx, float lly, float urx, float ury, float leading, int alignment)
I.e. the fifth parameter should have been the upper y coordinate of the area (obviously not a constant).
Effectively your "upper y" for the first lines actually denoted the bottom y and your "lower y" the top y. With the "correction" by adding dFontLine that created the desired results.
As soon as your "lower y" actually started denoting the bottom y, the top y remained constant, so all entries from there on were printed at the same height.
This also explains your observation from a comment:
Something I noticed is that is seems to work when I put the last coordinate to 0 instead of 302.
That way your "upper y" remains the bottom of the page. Thus, "upper" and "lower" remain switched but consistently so.
I assume you want something along this line:
ct.SetSimpleColumn(myText, dLeftSide, dBottom, dLeftSide + dCellSize, dBottom + dCellHeight, 0, Element.ALIGN_CENTER)
Related
I am using CorelDraw X7. I have a page containing many shapes and I wish to construct a macro to change it,
so that the bounding rectangle of alle shapes (the smallest one containing them all)
will have a common border of a given size.
I can find the size of the bounding rectangle and have tried to use the ActiveSelection.AlignAndDistribute sub
to move the shapes, but this sub has a lot of parameters, which I do not understand ("Help" does not help me).
My idea is this:
Specify the border, say pgBorder.
Get the width and height of the bounding rectangle, say shpsWidth and shpsHeight.
Move the shapes so that the lower left corner of the new bounding rectangle will have coordinates (pgBorder, pgBorder).
Reset the page size to shpsWidth + 2 * pgBorder resp. shpsHeight + 2 * pgBorder.
The shape bounding rectangle should now be surrounded with a border of size pgBorder.
This is what I have so far:
Sub GivePageCommonBorder()
Dim pgBorder As Double, shpsWidth As Double, shpsHeight As Double
Dim doc As Document
Dim pg As Page
Set doc = ActiveDocument
doc.Unit = cdrMillimeter
pgBorder = 20
Set pg = doc.ActivePage
' Select all shapes on the page
pg.Shapes.All.CreateSelection
shpsWidth = ActiveSelection.SizeWidth
shpsHeight = ActiveSelection.SizeHeight
' This is what I am lacking:
' Move the selection so its lower left corner has coordinates (pgBorder,pgBorder)
' Adjust page size
pg.SizeWidth = shpsWidth + 2 * pgBorder
pg.SizeHeight = shpsHeight + 2 * pgBorder
End Sub
Best wishes
Holger
I just stumbled upon the .Move method and constructed the following solution:
Sub GivePageCommonBorder()
Dim pgBorder As Double
Dim doc As Document
Dim pg As Page
Set doc = ActiveDocument
doc.Unit = cdrMillimeter
pgBorder = 5
Set pg = doc.ActivePage
pg.Shapes.All.CreateSelection
With ActiveSelection
pg.SizeWidth = .SizeWidth + 2 * pgBorder
pg.SizeHeight = .SizeHeight + 2 * pgBorder
.Move pgBorder - .LeftX, pgBorder - .BottomY
End With
End Sub
Holger
The Problem
I'm trying to figure out a way to get at which point in the content node the scroll pane's viewport is centered on.
To elaborate on the picture above, the big rectangle is the content (let's say a large image), and the small rectangle is the portion that is shown by the scroll pane. I'm trying to find x and y which would be coordinates from the top left of the content.
What I've Tried
My first thought was to use the getViewportBounds() method of the scroll pane and use its minX and maxX properties to determine the center x point:
Bounds b = scrollPane.getViewportBounds();
double centerX = (b.getMinX() + b.getMaxX()) / 2;
double centerY = (b.getMinY() + b.getMaxY()) / 2;
However, this doesn't work because these numbers are negative and don't seem to accurately describe the x and y I'm looking for anyways.
My next thought was to use the scroll pane's hValue and vValue to get the top left corner of the viewport relative to the content:
Bounds b = scrollPane.getViewportBounds();
double centerX = scrollPane.getHvalue() + b.getWidth() / 2;
double centerY = scrollPane.getVvalue() + b.getHeight() / 2;
This didn't work either though as the hValue and vValue seem to be way too large (when scrolled in only a few pixels, I'm getting numbers like 1600).
My Questions
I seem to have a fundamental misunderstanding of how the viewport works with a scroll pane.
What am I doing wrong here? Can someone explain where these numbers come from? How do I find x and y like in the picture above?
Let (x, y) be the be coordinates of the top, left point shown in the viewport. You can write this as
((contentWidth - viewportWidth) * hValueRel, (contentHeight - viewportHeight) * vValueRel)
vValueRel = vValue / vMax
hValueRel = hValue / hMax
This means assuming hmin and vmin remain 0 you can keep a circle in the center of like this:
// update circle position to be centered in the viewport
private void update() {
Bounds viewportBounds = scrollPane.getViewportBounds();
Bounds contentBounds = content.getBoundsInLocal();
double hRel = scrollPane.getHvalue() / scrollPane.getHmax();
double vRel = scrollPane.getVvalue() / scrollPane.getVmax();
double x = Math.max(0, (contentBounds.getWidth() - viewportBounds.getWidth()) * hRel) + viewportBounds.getWidth() / 2;
double y = Math.max(0, (contentBounds.getHeight() - viewportBounds.getHeight()) * vRel) + viewportBounds.getHeight() / 2;
Point2D localCoordinates = content.parentToLocal(x, y);
circle.setCenterX(localCoordinates.getX());
circle.setCenterY(localCoordinates.getY());
}
private Circle circle;
private Pane content;
private ScrollPane scrollPane;
#Override
public void start(Stage primaryStage) {
// create ui
circle = new Circle(10);
content = new Pane(circle);
content.setPrefSize(4000, 4000);
scrollPane = new ScrollPane(content);
Scene scene = new Scene(scrollPane, 400, 400);
// add listener to properties that may change
InvalidationListener l = o -> update();
content.layoutBoundsProperty().addListener(l);
scrollPane.viewportBoundsProperty().addListener(l);
scrollPane.hvalueProperty().addListener(l);
scrollPane.vvalueProperty().addListener(l);
scrollPane.hmaxProperty().addListener(l);
scrollPane.vmaxProperty().addListener(l);
scrollPane.hminProperty().addListener(l);
scrollPane.vminProperty().addListener(l);
primaryStage.setScene(scene);
primaryStage.show();
}
I'd like to preserve the position of a number of forms in the centre of the application, forms will be different sizes so below FormTop/FormLeft snippet can't be run again and again with the same effect. To do this I'm setting a public variable with the form's .top and .left values.
I get an error "Object Doesn't support this property or method", which seems odd as I was under the impression that the equation to make FormLeft would evaluate to a double data type. Both the lines work fine in the second section of code, what am I doing wrong?
Public FormTop As Double
Public FormLeft As Double
sub main()
CentreForm UserForm2
end sub
Sub CentreForm(UForm As UserForm)
With UForm
If FormTop = 0 And FormLeft = 0 Then
'*********Errors appear on the below two lines******************
FormLeft = Application.Left + (0.5 * Application.Width) - (0.5 * .Width)
FormTop = Application.Top + (0.5 * Application.Height) - (0.5 * .Height)
Debug.Print FormLeft, FormTop
End If
.StartUpPosition = 0
.Left = FormLeft
.Top = FormTop
End With
End Sub
sub IWork()
With UserForm2
.Left = Application.Left + (0.5 * Application.Width) - (0.5 * .Width)
.Top = Application.Top + (0.5 * Application.Height) - (0.5 * .Height)
end with
end sub
If you can't solve your problem based on comment suggestion please change this line:
Sub CentreForm(UForm As UserForm)
into
Sub CentreForm(UForm As Object)
I want to create many rectangles. This should be done automatically. How can I do this without typing thousands of values in my code? Is there an solution?
In my code I wrote every single coordinate point (4 points of each rectangle) manually in my vector "V".
Also how to connect them. "F"
And the value of each rectangle. "C"
My code is
clc
clear all
figure;
V = [0,0;1,0;1,1;0,1;5,5;10,5;10,10;5,10;2,2;4,2;4,4;2,4];
F = [1,2,3,4;5,6,7,8;9,10,11,12];%Dieser Vektor sagt mir in welcher Reihenfolge die Punkte
C = [50;24;99];
patch('Faces',F,'Vertices',V,'FaceVertexCData',C,'FaceColor','flat','EdgeColor','none') %Befehl fürs "zeichnen"
colormap(parula)
colorbar
You can use the following function to create a random rectangle, by randomly generating an (x, y) position for the bottom left corner, and randomly generating a width and height -
function rect = createRandomRectangle(maxX, maxY, minHeight, maxHeight, minWidth, maxWidth)
bottom = maxY * rand;
left = maxX * rand;
height = minHeight + rand * (maxHeight - minHeight);
width = minWidth + rand * (maxWidth - minWidth);
rect = [
left, bottom
left, bottom + height
left + width, bottom + height
left + width, bottom
];
end
Then you just need to take care of creating your V, F, C matrices (by calling createRandomRectangle in a loop) and plotting them.
I have a bunch of drawing areas (they are actually cairo surfaces, but I don't think it matters too much) in a scrolled window, and I would like to refresh the drawings. However, when I redraw the images, they are not shown till I scroll the window up and down. After that the figures are correct, so I have to conclude that the drawing routine itself is proper. I have also included a
while Gtk.events_pending():
Gtk.main_iteration()
loop to wait for all pending operations, but that does not solve the problem. Could someone point out to me what else is missing?
Thanks,
v923z
OK, so the larger chunks of the code. First, a class defining the a drawing area onto which I am going to paint (note that the body is not indented properly! I don't know how to indent larger pieces of code here):
class Preview:
def __init__(self):
self.frame = Gtk.Frame()
self.frame.set_shadow_type(Gtk.ShadowType.IN)
self.frame.show()
self.da = Gtk.DrawingArea()
self.da.set_size_request(200, 300)
self.da.connect('configure-event', self.configure_event)
self.da.connect('draw', self.on_draw)
self.frame.add(self.da)
self.da.show()
def configure_event(self, da, event):
allocation = da.get_allocation()
self.surface = da.get_window().create_similar_surface(cairo.CONTENT_COLOR,
allocation.width,
allocation.height)
cairo_ctx = cairo.Context(self.surface)
cairo_ctx.set_source_rgb(1, 1, 1)
cairo_ctx.paint()
return True
def on_draw(self, da, cairo_ctx):
cairo_ctx.set_source_surface(self.surface, 0, 0)
cairo_ctx.paint()
return True
pass
Next, the point where I actually create the drawing area. viewport_preview is a viewport created in glade.
self.previews = []
self.widget('viewport_preview').remove(self.vbox_preview)
self.vbox_preview = Gtk.VBox(homogeneous=False, spacing=8)
self.widget('viewport_preview').add(self.vbox_preview)
self.vbox_preview.show()
for page in self.pages:
preview = Preview()
self.vbox_preview.pack_start(preview.frame, False, False, 10)
self.previews.append(preview)
while Gtk.events_pending():
Gtk.main_iteration()
self.draw_preview(None)
return True
Then the function drawing the previews. This is really just a wrapper for the next function, and I needed this only because if I delete one entry in the previews, then I have to handle that case. I believe, the while loop at the end of this function is not necessary, for it will be at the end of the next one anyway.
def draw_preview(self, counter=None):
if counter is not None:
self.vbox_preview.remove(self.previews[counter].frame)
self.previews.pop(counter)
self.pages.pop(counter)
self.vbox_preview.show()
while Gtk.events_pending():
Gtk.main_iteration()
for i in range(len(self.pages)):
self.draw_note(self.previews[i].da, self.previews[i].surface, self.pages[i])
while Gtk.events_pending():
Gtk.main_iteration()
Finally, the drawing function itself:
def draw_note(self, widget, surface, page):
list_pos = '%d/%d'%(self.page + 1, len(self.pages))
self.widget('label_status').set_text(list_pos)
cairo_ctx = cairo.Context(surface)
cairo_ctx.set_source_rgb(page.background[0], page.background[1], page.background[2])
cairo_ctx.paint()
width, height = widget.get_size_request()
xmin, xmax, ymin, ymax = fujitsu.page_size(page)
factor = min(height / (2.0 * self.margin + ymax - ymin), width / (2.0 * self.margin + xmax - xmin))
factor *= 0.8
page.scale = factor
value = self.widget('adjustment_smooth').get_value()
#print value
for pen in page.pagecontent:
x = self.margin + pen.path[0][0] - xmin
y = self.margin + pen.path[0][1] - ymin
cairo_ctx.move_to(x * factor, y * factor)
if self.widget('checkbutton_smooth').get_active() == False:
[cairo_ctx.line_to((self.margin + x - xmin) * factor,
(self.margin + y - ymin) * factor) for x, y in pen.path]
else:
bezier_curve = bezier.expand_coords(pen.path, value)
x = self.margin + bezier_curve[0][0][0] - xmin
y = self.margin + bezier_curve[0][0][1] - ymin
cairo_ctx.move_to(x * factor, y * factor)
[cairo_ctx.curve_to((self.margin + control[1][0] - xmin) * factor,
(self.margin + control[1][1] - ymin) * factor,
(self.margin + control[2][0] - xmin) * factor,
(self.margin + control[2][1] - ymin) * factor,
(self.margin + control[3][0] - xmin) * factor,
(self.margin + control[3][1] - ymin) * factor)
for control in bezier_curve]
cairo_ctx.set_line_width(pen.thickness * self.zoom_factor)
cairo_ctx.set_source_rgba(pen.colour[0], pen.colour[1], pen.colour[2], pen.colour[3])
cairo_ctx.stroke()
cairo_ctx.rectangle(0, height * 0.96, width, height)
cairo_ctx.set_source_rgba(page.banner_text[0][0], page.banner_text[0][1], page.banner_text[0][2], page.banner_text[0][3])
cairo_ctx.fill()
cairo_ctx.move_to(width * 0.05, height * 0.99)
cairo_ctx.show_text(self.filename + ' ' + list_pos)
cairo_ctx.set_font_size(self.zoom_factor * 10.0)
xbearing, ybearing, twidth, theight, xadvance, yadvance = (cairo_ctx.text_extents(page.banner_text[3]))
cairo_ctx.move_to(width - 1.03 * twidth, height * 0.99)
cairo_ctx.show_text(page.banner_text[3])
cairo_ctx.set_source_rgba(0, 0, 0.9, 0.90)
cairo_ctx.stroke()
rect = widget.get_allocation()
widget.get_window().invalidate_rect(rect, False)
while Gtk.events_pending():
Gtk.main_iteration()
I think that's about it.
You could use gtk_widget_queue_draw_area or gdk_window_invalidate_rect.This will mark the widget (or rectangle) as dirty and once the main loop is idle expose event will be received where in you can redraw. From you description it appears the updates are happening on expose event so these APIs might be of use. Also you can check this sample from the cairo site where in you can see the usage of gtk_widget_queue_draw_area.
I have not used pygtk but from Google I found that the corresponding call for gtk_widget_queue_draw_area is gtk.Widget.queue_draw_area & for gdk_window_invalidate_rect is gtk.gdk.Window.invalidate_rect
Hope this helps!