Working with PDFKit: How to make page breaks? - swift

Looking for some direction
I'm working with PDFKit. Everything is going fine but having trouble finding the methods (documentation / WWDC / elsewhere ) on how to stop text from drawing at a certain y position and start the next page. Any saved references or a snippet of code would be a great help.

I dont know, wether this is the best way to implement your use case, but it worked for me.
When adding a new line into the PDF_Context, I recalculate a variable the keeps tracking of the current content height of my PDF_Page. When it exceeds a certain value, I create a NEW page, set the content height to zero and go on filling, ...
And you might wanna find some good answers, practices HERE -> RayWenderlich.
// my initial value
private var myCurrentPageContentHeight: CGFloat = 20
// in your PDF fill procedures add something like
myCurrentPageContentHeight += 40
// I also have a struct the encapsulates
// PageSize with padding, ...
enum PDF_PageSize {
case din_A4_Portrait
case din_A4_Landscape
// -------------------------------
// getPDF_TotalRect
// -------------------------------
func getPDF_TotalRect() -> CGRect {
switch self {
case .din_A4_Portrait : return CGRect(x: 0, y: 0, width: 595, height: 842)
case .din_A4_Landscape : return CGRect(x: 0, y: 0, width: 842, height: 595)
}
}
// ....
}

Related

Problem with stored properties in extensions in project from GitHub

I'm learning how to implement great CollectionViewPagingLayout templates in my project.
This one: https://github.com/amirdew/CollectionViewPagingLayout
First I specify which template to use (now I use "invertedCylinder") - and this part works well:
extension MovieCollectionViewCell: ScaleTransformView {
var scaleOptions: ScaleTransformViewOptions {
.layout(.invertedCylinder)
}
}
The problem appears when I try to modify the template. There is an extension, written by the creator of the Layout:
extension YourCell: ScaleTransformView {
var scaleOptions = ScaleTransformViewOptions(
minScale: 0.6,
scaleRatio: 0.4,
translationRatio: CGPoint(x: 0.66, y: 0.2),
maxTranslationRatio: CGPoint(x: 2, y: 0
)
}
I have tried to get rid of stored properties error and modify the code:
extension MovieCollectionViewCell: ScaleTransformView {
var scaleOptionsDetailed: ScaleTransformViewOptions {
minScale: 0.6,
scaleRatio: 0.4,
translationRatio: CGPoint(x: 0.66, y: 0.2),
maxTranslationRatio: CGPoint(x: 2, y: 0)
}
}
But this gives me more errors:
Redundant conformance of 'MovieCollectionViewCell' to protocol 'ScaleTransformView'
Cannot find 'minScale' in scope
Consecutive statements on a line must be separated by ';'
I understand that this is a question about basics. But it is already the second day im trying to solve the issue and would be very grateful for a guidance.
I don’t know why that repo includes stored properties on an extension. That is illegal and will likely always be illegal.
You could convert your variable scaleOptions to a computed property. To do that get rid of the equals sign. Then every time you reference that property it will run the code in the closure and generate a new value.
It is also possible to fake stored properties for extensions using associated values from the Objective-C runtime, but that is considered a hack and probably not a great idea.

removing array of GKGridGraphNodes from GKGridGraph unexpectedly slow

I am creating a 150 x 150 GKGridGraph for the purposes of A* pathfinding.
var graph = GKGridGraph(fromGridStartingAt: int2(0, 0), width: 150, height: 150, diagonalsAllowed: true)
I'm then looping through pixels of a reference map image and adding nodes to an array to be removed from the graph.
func getWalls(navMap: [bool], width: Int, height: Int) -> [GKGridGraphNode] {
var walls = [GKGridGraphNode]()
for(index, value) in navMap.enumberated() {
if(!value) { continue }
let x = index % width
let y = index / width
if let node = graph.node(atGridPosition: vector_int2(Int32(x),Int32(y))) {
walls.append(node)
}
}
return walls
}
up to this point everything is performant, but as soon as I attempt to remove the walls from the graph my program hangs for a LONG time.
graph?.remove(walls)
The thing that surprises me is that once the graph is setup, A* pathfinding on the grid runs super fast. While I expected pathfinding might be slow on such a large grid, I'm surprised that simply deleting a large chunk of nodes would cause such a significant performance hit.
So my question is WHY would removing a collection of nodes from GKGridGraph cause such a slowdown, and is there a more performant way to implement this operation?

Having issues with drawing to a frame buffer texture. It draws blank

I am in OpenGL es 2.0 with glKit trying to render to iOS devices.
Basically my goal is to instead of drawing to the main buffer draw to a texture. Then render that texture to the screen. I have been trying to follow another topic on so. Unfortunately they mention something about the power of two (im assuming with regards to resolution) but I don't know how to fix it. Anyway here is my swift interpretation of the code from that topic.
import Foundation
import GLKit
import OpenGLES
class RenderTexture {
var framebuffer:GLuint = 0
var tex:GLuint = 0
var old_fbo:GLint = 0
init(width: GLsizei, height: GLsizei)
{
glGetIntegerv(GLenum(GL_FRAMEBUFFER_BINDING), &old_fbo)
glGenFramebuffers(1, &framebuffer)
glGenTextures(1, &tex)
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), framebuffer)
glBindTexture(GLenum(GL_TEXTURE_2D), tex)
glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), nil)
glFramebufferTexture2D(GLenum(GL_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_TEXTURE_2D), tex, 0)
glClearColor(0, 0.1, 0, 1)
glClear(GLenum(GL_COLOR_BUFFER_BIT))
let status = glCheckFramebufferStatus(GLenum(GL_FRAMEBUFFER))
if (status != GLenum(GL_FRAMEBUFFER_COMPLETE))
{
print("DIDNT GO WELL WITH", width, " " , height)
print(status)
}
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), GLenum(old_fbo))
}
func begin()
{
glGetIntegerv(GLenum(GL_FRAMEBUFFER_BINDING), &old_fbo)
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), framebuffer)
}
func end()
{
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), GLenum(old_fbo))
}
}
Then as far as rendering I have some things going on.
A code that theoretically renders any texture full screen. This has been tested with two manually loaded pngs (using no buffer changes) and works great.
func drawTriangle(texture: GLuint)
{
loadBuffers()
//glViewport(0, 0, width, height)
//glClearColor(0, 0.0, 0, 1.0)
//glClear(GLbitfield(GL_COLOR_BUFFER_BIT) | GLbitfield(GL_DEPTH_BUFFER_BIT))
glEnable(GLenum(GL_TEXTURE_2D))
glActiveTexture(GLenum(GL_TEXTURE0))
glUseProgram(texShader)
let loc1 = glGetUniformLocation(texShader, "s_texture")
glUniform1i(loc1, 0)
let loc3 = glGetUniformLocation(texShader, "matrix")
if (loc3 != -1)
{
glUniformMatrix4fv(loc3, 1, GLboolean(GL_FALSE), &matrix)
}
glBindTexture(GLenum(GL_TEXTURE_2D), texture)
glDrawArrays(GLenum(GL_TRIANGLE_STRIP), 0, 6)
glDisable(GLenum(GL_TEXTURE_2D))
destroyBuffers()
}
I also have a function that draws a couple dots on the screen. You dont really need to see the methods but it works. This is how I am going to know that OpenGL is drawing from the buffer texture and NOT a preloaded texture.
Finally here is the gist of the code I am trying to do.
func initialize()
{
nfbo = RenderTexture(width: width, height: height)
}
fun draw()
{
glViewport(0, 0, GLsizei(width * 2), GLsizei(height * 2)) //why do I have to multiply for 2 to get it to work?????
nfbo.begin()
drawDots() //Draws the dots
nfbo.end()
reset()
drawTriangle(nfbo.tex)
}
At the end of all this all that is drawn is a blank screen. If there is any more code that would help you figure things out let me know. I tried to trim it to make it less annoying for you.
Note: Considering the whole power of two thing I have tried passing the fbo class 512 x 512 just in case it would make things work being a power of two. Unfortunately it didnt do that.
Another Note: All I am doing is going to be 2D so I dont need depth buffers right?
yesterday I saw exactly the same issue.
after struggling for hours, I found out why.
the trick is configuring your texture map with the following:
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE);
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE);
otherwise, you won't draw anything on the texture map.
the reason seems to be that while ios supports texture maps that are not power of 2. it requires GL_CLAMP_TO_EDGE. otherwise it won't work.
it should really report incomplete framebuffer. it took me quite long time to debug this problem!
here a related discussion:
Rendering to non-power-of-two texture on iPhone

Allegro5 showing text doesnt work

I have this kind of problem: I want to show the message "Hello!" near the centre of the Allegro screen before the game starts. Don't know why there is always only full window white colour after I compile the program not message "Hello!". I don't know why it doesn't show message "Hello!" . But if I erase the code between comment lines //***** program show the message "Hello!" well. Can someone can tell me how to solve this problem?
#include<allegro5\allegro5.h>
#include<allegro5\allegro_native_dialog.h>
#include<allegro5\allegro_font.h>
#include<allegro5\allegro_ttf.h>
#include<allegro5\allegro_primitives.h>
#include<iostream>
using namespace std;
int main(){
int ScreenWidth = 800, ScreenHeight = 600;
if (!al_init())
{
al_show_native_message_box(NULL, NULL, NULL, "Could not initialize Allegro 5", NULL, NULL);
return -1;
}
al_set_new_display_flags(ALLEGRO_WINDOWED);
ALLEGRO_DISPLAY *display = al_create_display(ScreenWidth, ScreenHeight);//creating window with dimensions: 800:600 px
al_set_window_position(display, 200, 100);//place from left top positioning the frame of display
al_set_window_title(display, "Try to catch me!");
if (!display)//show this message if sth wrong with display
{
al_show_native_message_box(display, "Sample Title", "Display Settings", "Display window was not created succesfully", NULL, ALLEGRO_MESSAGEBOX_ERROR);
return -1;
}
al_init_font_addon();//initialization font addon
al_init_ttf_addon();//initialization ttf(true type font) addon
//INTRO
ALLEGRO_FONT *fontOrbionBlack36 = al_load_font("fonts/Orbitron Black.ttf", 36, NULL);
al_draw_text(fontOrbionBlack36, al_map_rgb(44, 117, 255), ScreenWidth / 2, ScreenHeight / 2, ALLEGRO_ALIGN_CENTRE, "Hello!" );
al_rest(5.0);
//********************************************************************************
al_init_primitives_addon();
al_install_keyboard();
ALLEGRO_COLOR electricBlue = al_map_rgb(44, 117, 255);
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue, al_get_keyboard_event_source());
bool done = false;
int x = 10, y = 10;
int moveSpeed = 5;
while (!done)
{
ALLEGRO_EVENT events;
al_wait_for_event(event_queue, &events);
if (events.type = ALLEGRO_EVENT_KEY_DOWN)
{
switch (events.keyboard.keycode)
{
case ALLEGRO_KEY_DOWN:
y += moveSpeed;
break;
case ALLEGRO_KEY_UP:
y -= moveSpeed;
break;
case ALLEGRO_KEY_ESCAPE:
done = true;
break;
}
}
al_draw_rectangle(x, y, x + 20, y + 20, electricBlue, 2.0);
al_flip_display();
al_clear_to_color(al_map_rgb(0, 0, 0));
}
//*****************************************************************************************
al_flip_display();
al_rest(10.0);//rest is very simmilar to Sleep() it is waitg function, waiting 3s then close the allegro display
//al_destroy_font(fontOrbionBlack18);
al_destroy_font(fontOrbionBlack36);
al_destroy_display(display);//destroy the display at the end of the programm
return 0;
}
There are a few problems here that are preventing you from seeing the text.
You've set up a 'wait-for-event then draw' loop that is normally associated with the use of a timer, but you have no timer. This means that unless you trigger some sort of event (e.g. by pressing a keyboard key), al_wait_for_event will stall indefinitely and your program will never reach the call to al_flip_display. You can read more about timers here, but for the time being just know you will have to press a keyboard key to get this program to progress.
The ordering of your clear/draw/flip calls is a bit confusing.
Try something like this within your main loop:
al_clear_to_color(al_map_rgb(0, 0, 0));`
al_draw_rectangle(x, y, x + 20, y + 20, electricBlue, 2.0);
al_draw_text(fontOrbionBlack36, al_map_rgb(44, 117, 255), ScreenWidth / 2, ScreenHeight / 2, ALLEGRO_ALIGN_CENTRE, "Hello!" );
al_flip_display();
In other words, clear first, then draw, then flip. Currently you only draw the text once (before your main loop). This means that clearing the screen will clear the text -- you need to draw the text every frame after clearing the screen but before flipping the display.
If you change those two things, you should be able to see the text after pressing a keyboard button. However, there are a few other changes that might help:
I'd remove those calls to al_rest unless they serve a useful purpose. Currently they just make you wait 5 seconds before you start your main loop (meaning you won't be able to see the text immediately even if you do press a key).
There's no reason to flip the display after your main loop exits, although I guess this was left over from your earlier experiments with showing text.
When checking the event type, use events.type == ALLEGRO_EVENT_KEY_DOWN instead of events.type = ALLEGRO_EVENT_KEY_DOWN. Currently, you're actually assigning the value ALLEGRO_EVENT_KEY_DOWN to events.type, overwriting whatever its original value was. Your compiler may (and probably should) have warned you about this.

Drag, drop and shape rotation with Raphael JS

I'm using RaphaelJS 2.0 to create several shapes in a div. Each shape needs to be able to be dragged and dropped within the bounds of the div, independently. Upon double clicking a shape, that shape needs to rotate 90 degrees. It may then be dragged and dropped and rotated again.
I've loaded some code onto fiddler: http://jsfiddle.net/QRZMS/. It's basically this:
window.onload = function () {
var angle = 0;
var R = Raphael("paper", "100%", "100%"),
shape1 = R.rect(100, 100, 100, 50).attr({ fill: "red", stroke: "none" }),
shape2 = R.rect(200, 200, 100, 50).attr({ fill: "green", stroke: "none" }),
shape3 = R.rect(300, 300, 100, 50).attr({ fill: "blue", stroke: "none" }),
shape4 = R.rect(400, 400, 100, 50).attr({ fill: "black", stroke: "none" });
var start = function () {
this.ox = this.attr("x");
this.oy = this.attr("y");
},
move = function (dx, dy) {
this.attr({ x: this.ox + dx, y: this.oy + dy });
},
up = function () {
};
R.set(shape1, shape2, shape3, shape4).drag(move, start, up).dblclick(function(){
angle -= 90;
shape1.stop().animate({ transform: "r" + angle }, 1000, "<>");
});
}
The drag and drop is working and also one of the shapes rotates on double click. However, there are two issues/questions:
How can I attach the rotation onto each shape automatically without having to hard-code each item reference into the rotate method? I.e. I just want to draw the shapes once, then have them all automatically exposed to the same behaviour, so they can each be dragged/dropped/rotated independently without having to explicitly apply that behaviour to each shape.
After a shape has been rotated, it no longer drags correctly - as if the drag mouse movement relates to the original orientation of the shape rather than updating when the shape is rotated. How can I get this to work correctly so that shapes can just be dragged and rotated many times, seamlessley?
Many thanks for any pointers!
I've tried several times to wrap my head around the new transform engine, to no avail. So, I've gone back to first principles.
I've finally managed to correctly drag and drop an object thats undergone several transformations, after trying to work out the impact of the different transformations - t,T,...t,...T,r,R etc...
So, here's the crux of the solution
var ox = 0;
var oy = 0;
function drag_start(e)
{
};
function drag_move(dx, dy, posx, posy)
{
r1.attr({fill: "#fa0"});
//
// Here's the interesting part, apply an absolute transform
// with the dx,dy coordinates minus the previous value for dx and dy
//
r1.attr({
transform: "...T" + (dx - ox) + "," + (dy - oy)
});
//
// store the previous versions of dx,dy for use in the next move call.
//
ox = dx;
oy = dy;
}
function drag_up(e)
{
// nothing here
}
That's it. Stupidly simple, and I'm sure it's occurred to loads of people already, but maybe someone might find it useful.
Here's a fiddle for you to play around with.
... and this is a working solution for the initial question.
I solved the drag/rotate issue by re-applying all transformations when a value changes. I created a plugin for it.
https://github.com/ElbertF/Raphael.FreeTransform
Demo here:
http://alias.io/raphael/free_transform/
As amadan suggests, it's usually a good idea to create functions when multiple things have the same (initial) attributes/properties. That is indeed the answer to your first question. As for the second question, that is a little more tricky.
When a Rapheal object is rotated, so is the coordinate plane. For some reason, dmitry and a few other sources on the web seem to agree that it's the correct way to implement it. I, like you, disagree. I've not managed to find an all round good solution but I did mange to create a work around. I'll briefly explain and then show the code.
Create a custom attribute to store the current state of rotation
Depending on that attribute you decide how to handle the move.
Providing that you are only going to be rotating shapes by 90 degrees (if not it becomes a lot more difficult) you can determine how the coordinates should be manipulated.
var R = Raphael("paper", "100%", "100%");
//create the custom attribute which will hold the current rotation of the object {0,1,2,3}
R.customAttributes.rotPos = function (num) {
this.node.rotPos = num;
};
var shape1 = insert_rect(R, 100, 100, 100, 50, { fill: "red", stroke: "none" });
var shape2 = insert_rect(R, 200, 200, 100, 50, { fill: "green", stroke: "none" });
var shape3 = insert_rect(R, 300, 300, 100, 50, { fill: "blue", stroke: "none" });
var shape4 = insert_rect(R, 400, 400, 100, 50, { fill: "black", stroke: "none" });
//Generic insert rectangle function
function insert_rect(paper,x,y, w, h, attr) {
var angle = 0;
var rect = paper.rect(x, y, w, h);
rect.attr(attr);
//on createion of the object set the rotation position to be 0
rect.attr({rotPos: 0});
rect.drag(drag_move(), drag_start, drag_up);
//Each time you dbl click the shape, it gets rotated. So increment its rotated state (looping round 4)
rect.dblclick(function(){
var pos = this.attr("rotPos");
(pos++)%4;
this.attr({rotPos: pos});
angle -= 90;
rect.stop().animate({transform: "r" + angle}, 1000, "<>");
});
return rect;
}
//ELEMENT/SET Dragger functions.
function drag_start(e) {
this.ox = this.attr("x");
this.oy = this.attr("y");
};
//Now here is the complicated bit
function drag_move() {
return function(dx, dy) {
//default position, treat drag and drop as normal
if (this.attr("rotPos") == 0) {
this.attr({x: this.ox + dx, y: this.oy + dy});
}
//The shape has now been rotated -90
else if (this.attr("rotPos") == 1) {
this.attr({x:this.ox-dy, y:this.oy + dx});
}
else if (this.attr("rotPos") == 2) {
this.attr({x: this.ox - dx, y: this.oy - dy});
}
else if (this.attr("rotPos") == 3) {
this.attr({x:this.ox+dy, y:this.oy - dx});
}
}
};
function drag_up(e) {
}
I can't really think of clear concise way to explain how the drag_move works. I think it's probably best that you look at the code and see how it works. Basically, you just need to work out how the x and y variables are now treated from this new rotated state. Without me drawing lots of graphics I'm not sure I could be clear enough. (I did a lot of turning my head sideways to work out what it should be doing).
There are a few drawbacks to this method though:
It only works for 90degree rotations (a huge amount more calculations would be needed to do 45degrees, nevermind any given degree)
There is a slight movement upon drag start after a rotation. This is because the drag takes the old x and y values, which have been rotated. This isn't a massive problem for this size of shape, but bigger shapes you will really start to notice shapes jumping across the canvas.
I'm assuming the reason that you are using transform is that you can animate the rotation. If this isn't necessary then you could use the .rotate() function which always rotates around the center of the element and so would eliminate the 2nd drawback I mentioned.
This isn't a complete solution, but it should definitely get you going along the correct path. I would be interested to see a full working version.
I've also created a version of this on jsfiddle which you can view here: http://jsfiddle.net/QRZMS/3/
Good luck.
I usually create an object for my shape and write the event handling into the object.
function shape(x, y, width, height, a)
{
var that = this;
that.angle = 0;
that.rect = R.rect(x, y, width, height).attr(a);
that.rect.dblclick(function() {
that.angle -= 90;
that.rect.stop().animate({
transform: "r" + that.angle }, 1000, "<>");
});
return that;
}
In the above, the constructor not only creates the rectangle, but sets up the double click event.
One thing to note is that a reference to the object is stored in "that". This is because the "this" reference changes depending on the scope. In the dblClick function I need to refer to the rect and angle values from my object, so I use the stored reference that.rect and that.angle
See this example (updated from a slightly dodgy previous instance)
There may be better ways of doing what you need, but this should work for you.
Hope it help,
Nick
Addendum: Dan, if you're really stuck on this, and can live without some of the things that Raphael2 gives you, I'd recommend moving back to Raphael 1.5.x. Transforms were just added to Raphael2, the rotation/translation/scale code is entirely different (and easier) in 1.5.2.
Look at me, updating my post, hoping for karma...
If you don't want to use a ElbertF library, you can transform Cartesian Coordinates in Polar Coordinates.
After you must add or remove the angle and transform again in Cartesian Coordinate.
We can see this example with a rect rotate in rumble and moved.
HTML
<div id="foo">
</div>
JAVASCRIPT
var paper = Raphael(40, 40, 400, 400);
var c = paper.rect(40, 40, 40, 40).attr({
fill: "#CC9910",
stroke: "none",
cursor: "move"
});
c.transform("t0,0r45t0,0");
var start = function () {
this.ox = this.type == "rect" ? this.attr("x") : this.attr("cx");
this.oy = this.type == "rect" ? this.attr("y") : this.attr("cy");
},
move = function (dx, dy) {
var r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
var ang = Math.atan2(dy,dx);
ang = ang - Math.PI/4;
dx = r * Math.cos(ang);
dy = r * Math.sin(ang);
var att = this.type == "rect" ? { x: this.ox + dx, y: this.oy + dy} : { cx: this.ox + dx, cy: this.oy + dy };
this.attr(att);
},
up = function () {
};
c.drag(move, start, up);?
DEMO
http://jsfiddle.net/Ef83k/74/
my first thought was to use getBBox(false) to capture the x,y coordinates of the object after transform, then removeChild() the original Raphael obj from the canvas, then redraw the object using the coordinate data from getBBox( false ). a hack but i have it working.
one note though: since the object the getBBox( false ) returns is the CORNER coordinates ( x, y) of the object you need to calculate the center of the re-drawn object by doing ...
x = box['x'] + ( box['width'] / 2 );
y = box['y'] + ( box['height'] / 2 );
where
box = shapeObj.getBBox( false );
another way to solve the same problem