domenica 28 settembre 2014

Trim Marks at distance

FitPlot 5.1

Trim Mark Offset Implementation

As requested from a user, It has been easy to add an offset distance value to be used together when trim marks style is selected.

Trim marks are thin lines that when printed facilitate the cutting.


Now these thin lines can be distanced from the border of a user given value (in the current unit of measure).
Negative values move lines inside (sliding on relative border fixed distance).
Positive values move outside.

 offset = -1(cm)     offset = +1(cm)

To insert the trim marks offset value, it may be necessary to expand the info panel.
The field is enabled when trim marks style is selected.



Don't worry about packing or aligning, trim marks are considered in the bound of an image (since version 5).

Here's the code (called by - (void) drawElement: (NSRect) inRect inContext: (CGContextRef) contextCG in the FP_Image class).

#pragma mark DRAW STYLES
- (void) drawStyles
{
    NSInteger trueStyle = [self imageStyle];
    if ([self linkIsBroken]) //trattamento speciale per fileNotFound
        [self setImageStyle:2];
    
    [self impostaStile: imageStyle];
    
    //ricavo l'indice dash
    NSInteger iDash = styleDash/8-1;
    NSInteger iStroke = styleStroke/32-1;
    
    float scala = [self imageScale];
    float spessore = 0;
    if (iStroke >=0) spessore = [[[[NSApp delegate] strokes ] objectAtIndex: iStroke] floatValue] / scala;
    
    //trim
    if (styleTrim) {
        [[NSColor blackColor] set];
        NSBezierPath *path = [NSBezierPath bezierPath];
        
        float trimSliding = imageTrimOffset/scala;
        float trimOffset = imageTrimOffset > 0 ? trimSliding:0;
        
        // crocino alto / sx
        [path moveToPoint: NSMakePoint([self cropRect].origin.x-trimOffset-CM2PX(0.2)/scala,
                                       [self cropRect].origin.y-trimSliding)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x-trimOffset-CM2PX(0.7)/scala,
                                       [self cropRect].origin.y-trimSliding)];

        [path moveToPoint: NSMakePoint([self cropRect].origin.x-trimSliding,
                                       [self cropRect].origin.y-trimOffset-CM2PX(0.2)/scala)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x-trimSliding,
                                       [self cropRect].origin.y-trimOffset-CM2PX(0.7)/scala)];

        //crocino basso / sx
        [path moveToPoint: NSMakePoint([self cropRect].origin.x-trimOffset-CM2PX(0.2)/scala,
                                       [self cropRect].origin.y+trimSliding+[self cropRect].size.height)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x-trimOffset-CM2PX(0.7)/scala,
                                       [self cropRect].origin.y+trimSliding+[self cropRect].size.height)];
        
        [path moveToPoint: NSMakePoint([self cropRect].origin.x-trimSliding,
                                       [self cropRect].origin.y+trimOffset+[self cropRect].size.height+CM2PX(0.2)/scala)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x-trimSliding,
                                       [self cropRect].origin.y+trimOffset+[self cropRect].size.height+CM2PX(0.7)/scala)];

        // crocino alto / dx
        [path moveToPoint: NSMakePoint([self cropRect].origin.x+trimOffset+[self cropRect].size.width+CM2PX(0.2)/scala,
                                       [self cropRect].origin.y-trimSliding)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x+trimOffset+[self cropRect].size.width+CM2PX(0.7)/scala,
                                       [self cropRect].origin.y-trimSliding)];
        
        [path moveToPoint: NSMakePoint([self cropRect].origin.x+trimSliding+[self cropRect].size.width,
                                       [self cropRect].origin.y-trimOffset-CM2PX(0.2)/scala)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x+trimSliding+[self cropRect].size.width,
                                       [self cropRect].origin.y-trimOffset- CM2PX(0.7)/scala)];
        
        // crocino basso / dx
        [path moveToPoint: NSMakePoint([self cropRect].origin.x+[self cropRect].size.width+trimOffset+CM2PX(0.2)/scala,
                                       [self cropRect].origin.y+[self cropRect].size.height+trimSliding)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x+[self cropRect].size.width+trimOffset+CM2PX(0.7)/scala,
                                       [self cropRect].origin.y+[self cropRect].size.height+trimSliding)];
        
        [path moveToPoint: NSMakePoint([self cropRect].origin.x+trimSliding+[self cropRect].size.width,
                                       [self cropRect].origin.y+trimOffset+[self cropRect].size.height+CM2PX(0.2)/scala)];
        [path lineToPoint: NSMakePoint([self cropRect].origin.x+trimSliding+[self cropRect].size.width,
                                       [self cropRect].origin.y+trimOffset+[self cropRect].size.height+CM2PX(0.7)/scala)];

        [path setLineWidth: HAIRLINE/scala];
        [path stroke];
        
        NSBezierPath * tempPath = [NSBezierPath bezierPathWithRect:NSMakeRect(cropRect.origin.x-CM2PX(0.7)/scala-trimOffset,
                                                                              cropRect.origin.y-CM2PX(0.7)/scala-trimOffset,
                                                                              cropRect.size.width+2*CM2PX(0.7)/scala+trimOffset*2,
                                                                              cropRect.size.height+2*CM2PX(0.7)/scala+trimOffset*2)] ;

        NSAffineTransform * transform = [ NSAffineTransform transform] ;
        [transform appendTransform:localTF];
        [tempPath transformUsingAffineTransform:transform];
        
        [self setBounds:NSUnionRect([tempPath bounds], [self bounds])]; //aumento i bounds considerando le linee di taglio
    }
    
    if (iDash >= 0)
    {
        [[NSColor blackColor] set];
        NSBezierPath *contourRect = [NSBezierPath bezierPathWithRect:[self cropRect]];
        
        if (styleDash == 32) {
            CGFloat array [4];
            array[0] = [[[[NSApp delegate] dashes ] objectAtIndex: iDash] floatValue]/scala;
            array[1] =  [[[[NSApp delegate] dashgaps ] objectAtIndex: iDash] floatValue]/scala;
            array[2] = array[0]/4;
            array[3] =  array[1];
            [contourRect setLineDash: array count: 4 phase: 0.0];
        }
        else
        {
            CGFloat array [2];
            array[0] = [[[[NSApp delegate] dashes ] objectAtIndex: iDash] floatValue]/scala;
            array[1] =  [[[[NSApp delegate] dashgaps ] objectAtIndex: iDash] floatValue]/scala;
            [contourRect setLineDash: array count: 2 phase: 0.0];
        }
        
        [contourRect setLineWidth: spessore];
        [contourRect stroke];
    }
    
    if (iStroke >= 0 && iDash <0)
    {
        [[NSColor blackColor] set];
        NSBezierPath *contourRect = [NSBezierPath bezierPathWithRect:[self cropRect]];
        
        [contourRect setLineWidth: spessore];
        [contourRect stroke];
    }
    
    
    NSBezierPath * tempPath = [NSBezierPath bezierPathWithRect:NSMakeRect(cropRect.origin.x-spessore/2,
                                                                          cropRect.origin.y-spessore/2,
                                                                          cropRect.size.width+spessore,
                                                                          cropRect.size.height+spessore)] ;
    
    
    NSAffineTransform * transform = [ NSAffineTransform transform] ;
    //[ transform scaleBy: 1/imageGroupScale];

    [transform appendTransform:localTF];
    
    [tempPath transformUsingAffineTransform:transform];
    
    [self setBounds:NSUnionRect([tempPath bounds], [self bounds])];  //aumento i bounds considerando lo spessore
    //NSLog(@"setBounds in drawStyle");
    
    
    //NSGraphicsContext *contextStyle = [NSGraphicsContext currentContext];
    //[contextStyle saveGraphicsState];
    
    [self setImageStyle:trueStyle];
    
    //[contextStyle restoreGraphicsState];
}



venerdì 26 settembre 2014

2-Up Saddle Stitch update

FitPlot 5.1

2-Up Saddle Stitch

Think about a 100 pages PDF: you are able to use the 2-up Saddle imposition, then print front / back the result, but, when you goes to stitch, you realise that the pages thickness is too much to bend and stitch.
The user who suggest me to find a solution, had 991 pages to print grouped in booklets by 24 pages (to give to the bookbinder), so a solution had to be found…
Reverse page order

Technically speaking, the imposition is done by few actions / procedures:
Reverse page order
2-Up Saddle Stitch is an imposition mode that is present in FitPlot since the first imposition implementation (FitPlot 2.5, 2008-05-31).
It has remained unchanged until now, when, on a user suggestion, I have introduced the option to group the imposition in groups (booklets).


Of course, there has always been the possibility to impose from page 1 to page X then from page X to Y … and then from Z to the end, but…



- (IBAction) checkImpo:(id) sender
has the task to control the user input in the imposition dialog. Based on user input, checkImpo  calculates the number of empty pages to add (to complete the 24 pages booklet, in this case), to show the number of sheets needed etc.


- (IBAction) imposition:(id)sender
When Confirm is pressed, imposition is called. It is an intermediate action to leave things that worked unchanged (2-Up Perfect Bound and 2-Up Saddle Stitch with a sigle booklet as it was before).
The two old modes (just above mentioned) are then sent to the old action impose: (see below) while the 2-Up Saddle Stitch with booklet splitting has to enter a loop and at each cycle it calls impose: changing the Y (multiplying by the page vertical size).

- (IBAction) impose:(id) sender pdf:(FP_Image*) elem startingFromY:(float) Y

It works! Few changes in a well structured IBAction allowed to easily implement such useful new feature (and make happy Mr. Franco!).
The only problem, still unresolved, is that 991 pages means 41 loops of impose: and it is a pain to arrive at an end. Each impose: has its own undoManager and this cause a RAM and CPU payment in terms of process time.
But it works and, thanks to the 64 bit architecture, it doesn't crash (at least in my tests), just take a coffee while waiting.

Reverse page order

Asked from a friend from Pakistan, in its FitPlot customer review, it has been easy to introduce a reverse page order check box.

- (void) imponi: (NSInteger) sinistra
         destra: (NSInteger) destra
           dove: (NSPoint) quadrante
       centrato: (NSPoint) centro
     daElemento: (FP_Image*) elemento
   conRotazione: (BOOL) rotazione
    perLePagine: (NSRange) pagineEsistenti
     inversione:(BOOL)inverti

Just introduced the inversione:(BOOL)inverti variable in the core of the imposition task and the reversion is obtained.
For what I understand, inversion means that the last page become first and so all other pager are exchanged symmetrically, obtained by the following simple function:

- (NSInteger) inverti:(NSInteger) numero specchia:(BOOL)specchia da:(int)impoDa a:(int)impoA
{
    if (!specchia)
        return numero;
    
    return impoDa-1+impoA-1-numero;
}

I just need a confirmation that what I have done is right. The request was the following:
Arabic or Right hand book support - I think this is an affordable alternate to Imposition Studio. It lacks just right to left books support. Shibli313