Farbtafeln mit InDesign

Manchmal geht probieren über studieren. Zum Beispiel wenn man eine neue Sonderfarbe mit Euroskala-Farben überdrucken möchte. Zwar gibt es am Bildschirm eine „Überdruck simulieren“ Funktion, jedoch ist diese nicht zuverlässig (das kann verschiedene Gründe haben, z.B. ein zu kleiner Farbraum des Bildschirms). Auch ein Digitalproof kann nur bedingt das tatsächliche Druckergebnis simulieren (auch hier kann der Farbraum zu klein sein). Sonderfarben verhalten sich manchmal im Überdruck mit anderen Farben ganz anders als erwartet (durch Rückspaltung usw.)

Es gibt also Situationen, in denen man um einen Probedruck nicht herum kommt. Praktisch wäre eine Farbtafel, die alle Nuancen der Sonderfarbe im Überdruck mit den Euroskala-Farben enthält. Solch eine Farbtafel kann man natürlich manuell mit z.B.  InDesign herstellen. Das ist aber mühsam und langweilig.

Spannender und weitaus produktiver wirds mit einem InDesign-Script. InDesign versteht zwei Scriptsprachen: AppleScript und Javascript. Ich nutze Javascript aus Gründen (vor allem, weil ich keine teuren Äpfel mag).
Das Script erzeugt ein neues Dokument und erstellt Seiten im Format 21x21cm und plaziert darauf die Testfelder. Pro Feld wird ein neues Farbfeld angelegt. Pro Sonderfarb-Abstufung muss zusätzlich ein Tint erstellt werden und das Atrribut „Fläche überdrucken“ auf das Rechteck angewendet werden.

Nach wenigen Sekunden zeigt InDesign die fertige Farbtafel an:

Farbtafel Simulation
Farbtafel Cyan/Magenta und simulierter Sonderfarbe

Das ist der Quellcode zum Erzeugen der Tafel, das Script im InDesign Scripting-Panel ablegen:

/**
 *  Farbtafeln erstellen
 *  change variables below according to your needs
 */

main();
function main (){

	// define page size in mm
	var pw = 210;
	var ph = 210;

	// define number of boxes in x and y direction
	var num_row = 21;
	var num_col = 21;
	
	var stepX = (pw-10) / num_col; // size of one box, less 5mm page margin
	var stepY = (ph-10) / num_row;

	// create document
	var doc = app.documents.add({
			documentPreferences:{
				pageHeight:ph,
				pageWidth:pw,
				facingPages:false // for simplicity don´t create facing pages
			}
	});
	var pg = doc.pages.item(0); // get the page
	
	// define initial coordinates for first box
	var x1 = stepX / 2.0;
	var y1 = stepY / 2.0;
	var x2 = x1 + stepX;
	var y2 = y1 + stepY;

	
	// define colors and paperwhite
	var cyan = 0;
	var magenta = 0;
	var yellow = 0;
	var black = 0;
	var paper = color_add (doc, "paper color ["+0+";"+0+";"+0+";"+0+"]", ColorModel.PROCESS, [0, 0, 0, 0]);
	
	// define spot color
	var special =doc.colors.add();
    special.properties = {
            name:"AndiGun Spezialtoner", 
            model:ColorModel.SPOT,
            space:ColorSpace.CMYK,
            colorValue:[0,0,100,0] // alternate color is 100% yellow
			};
			
	// define text color
	var text =doc.colors.add();
    text.properties = {
            name:"Text", 
            model:ColorModel.PROCESS,
            space:ColorSpace.CMYK,
            colorValue:[0,0,0,100] // 100% black
		};
	
	// start procedure
	for (var i = 0; i < num_col; i++) { // for each column
		for (var j = num_row-1; j >= 0; j--) { // for each row
			
			// adjust as needed
			cyan = (j*5);
			magenta = (i*5);
			//black = j*5;
			
			var color_name = "cmyk color ["+cyan+";"+magenta+";"+yellow+";"+black+"]";
            
              if (i == 0) {
                        var newTextFrame = pg.textFrames.add(); 
                        newTextFrame.contents = cyan+""; 
                        newTextFrame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
                        var myText = newTextFrame.parentStory.paragraphs.item(0);
                        myText.pointSize = 8;
                        myText.justification = Justification.CENTER_ALIGN;  
                        newTextFrame.geometricBounds = [0, x1 , stepY/2, x2]; // Top, Left, Bottom, Right 
              }            
          
			var color = color_add (doc, color_name, ColorModel.PROCESS, [cyan, magenta, yellow, black]);
			
			var rect = pg.rectangles.add({geometricBounds:[y1,x1,y2,x2]});
			rect.fillColor = color;
			rect.strokeWeight = 1;
			rect.strokeColor = paper;
			x1 = x2;
			x2 = x2 + stepX;
		} // end of row
		
		y1 = y2;
		y2 = y2 + stepY;
		x1 = stepX / 2.0;
		x2 = x1 + stepX;
		
	} // end of columns
	
	var tint = 0;
    x1 = stepX / 2;
	y1 = stepY / 2;
	x2 = x1 + stepX;
	y2 = y1 + stepY;    
    
	// create array of tints for special color
    var tints = Array();
       for (var i = 1; i < num_col-1; i++) {
               var specialTint = doc.tints.add (special);
               specialTint.tintValue = i * 5;
               tints[i] = specialTint;
           }
    
    for (var i = 0; i < num_col; i++) {
		for (var j = num_row-1; j >= 0; j--) {
			    
			tint = i*5;
			
            if (j == 0) {
                        var newTextFrame = pg.textFrames.add(); 
                        newTextFrame.contents = tint+""; 
                        newTextFrame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
                        var myText = newTextFrame.parentStory.paragraphs.item(0);
                        myText.pointSize = 8;
                        myText.justification = Justification.CENTER_ALIGN;  
                        newTextFrame.geometricBounds = [y1, x2 , y2, x2+stepX/2]; // Top, Left, Bottom, Right 
              }            
                
              if (tint > 0) { // tints have to be greater than 0
                var rect = pg.rectangles.add({geometricBounds:[y1,x1,y2,x2]});
                if (tint < 100) { 
                     rect.fillColor = tints[i];    
                     }
                 else { // with 100% it´s not a tint, it´s the actual color
                     rect.fillColor = special;    
                     }
                     
                rect.overprintFill=true; // set overprint flag
                rect.strokeWeight = 1;
                rect.strokeColor = paper;
            }
			x1 = x2;
			x2 = x2 + stepX;
            
		}
		y1 = y2;
		y2 = y2 + stepY;
		x1 = stepX / 2.0;
		x2 = x1 + stepX;
	}
	
}; // end of function main

/*
* function adds color swatch to document and returns color
*/
function color_add(myDocument, myColorName, myColorModel, myColorValue){
	
	if(myColorValue.length == 3) myColorSpace = ColorSpace.RGB;
	else myColorSpace = ColorSpace.CMYK;
	
	myColor = myDocument.colors.item(myColorName);
	myColor = myDocument.colors.add();
	myColor.properties = {name:myColorName, model:myColorModel, space:myColorSpace ,colorValue:myColorValue};
	
	return myColor;
}

Squatopoly

Dieses Spiel ist besetzt!

Spielt solidarisch und besetzt gemeinsam ein berühmtes Gesellschaftsspiel ähnlichen Namens. Squatopoly ist ein Spiel für 2-8 Personen und ist dabei ganz anders als sein Vorbild.

Warum um den Kuchen streiten, wenn wir selber welchen backen können? Mit Squatopoly besitzt Du kein Stück vom Kuchen, sondern Du besetzt die ganze Bäckerei! Und zwar im Sitzen, am Wohnzimmertisch, gegen Samstag Abend, mit der ganzen Familie, bei Kerzenschein und Heiterkeit.

Ohren gespitzt: Squatopoly kannst Du nicht sofort out of the box spielen, denn Squatopoly ist ein Paket um ein berühmtes Gesellschaftsspiel ähnlichen Namens zu besetzen. Solltest Du kein Spiel besitzen, das Du besetzen könntest, ist Deine Kreativität gefragt: ein Spielfeld lässt sich auch leicht selbst anfertigen: klebe die Squatopoly-Sticker einfach auf ein großes Stück Pappe (Pizzaschachtel, Umzugskarton, Flachbildschirmverpackung). Drucke Dein eigenes Geld und benutze die Bauklötzchen der Kinder oder die Kronkorken Deiner Gäste als Häuser und Spielsteine. Und leihe Dir zwei Würfel von Nachbarn aus!

Lade Freunde ein und dann besetzt gemeinsam – denn allein machen sie Dich ein. Je mehr Mitspieler*innen, desto besser! Nur die Größe Deiner Bude begrenzt die Anzahl der Spielenden.

Und das Schönste ist: Ihr könnt nur alle gemeinsam gewinnen; oder alle gemeinsam verlieren, denn Ihr kämpft vereint gegen die Windmühlen der bürokratischen Würfel.

Interesse:

Mehr Informationen und den Webshop findest Du hier: http://squatopoly.de/

GunImposer

GunImposer ist ein Programm zum Herstellen von Sammelformen. Seine Stärken sind Geschwindigkeit und die Möglichkeit sehr viele Seiten verarbeiten zu können.

Problem:

  • Ein pdf-Dokument mit 9999 Gutscheinen soll gedruckt werden
  • Die Reihenfolge soll erhalten bleiben
  • Die variablen Daten sollen einen komplizierten Hintergrund bekommen
  • Zwischenschnitte müssen definierbar sein
  • Berücksichtigung von Beschnittzugabe
  • Keine Skalierung der Daten vornehmen (wie bei Acrobat Mehrfach-Ausgabe)

Lösung:

Java-Programm GunImposer mit Verwendung der Apache pdfBox-Library. Mit GunImposer erstellt man fertige Druckbögen mit variablen Daten. Mit Acrobat kann man der erzeugten pdf-Datei einen Hintergrund für alle Seiten verpassen. Dadurch wird der Hintergrund nur einmal hinzugefügt wodurch die Dateigröße enorm sinkt, da alle sich wiederholenden Inhalte nur einmal gespeichert werden. Oder man nutzt die pdf ohne Hintergrund und verwendet dann den Variablen Datendruck VDP, wenn man zB Fiery Command WorkStation hat.

Workflow:

  • Bogenformat einrichten
  • Schema Editor für Nutzenanordnung
  • Datei laden (alle Seiten sollten die gleiche Größe haben)
  • Sortierung wählen, so dass Stapel in Reihenfolge gedruckt werden
  • Optional: Seiten duplizieren
  • Optional: Schnittmarken erzeugen
  • Feature: GunImposer speichert die Eingaben und lädt sie beim nächsten Start neu
GunImposer Schema-Editor

Ergebnis:

GunImposer Druckbogen Seite 1
GunImposer Druckbogen Seite 1250

Auf der letzten Seite fehlt ein Nutzen, da das Dokument 9999 Seiten hatte. Die härteste Nuss an dem Programm war die korrekte Sortierung zu erzielen – einem eigentlich einfachen mathematischen Problem.

Sourcecode:

Wie berechnet sich die Anzahl der Druckbögen?

int allNutzen = cols*rows;	    	
int impoPages = (int)Math.ceil((totalPageNum*duplicates) / (float)allNutzen);
getLblFileInfo().setText((totalPageNum*duplicates)+" Seiten zu "+allNutzen+" Nutzen auf "+impoPages+" Druckbogen");

Interesse:

Frag mich nach einer Kopie des Programms.

GunPrinter

In der Druckvorstufe wird viel geplottet. Ein Plott dient zur Kontrolle der montierten Daten, bevor sie auf Druckplatte belichtet werden. Man kann so die korrekte Positionierung und Seitenfolge prüfen. Schwierig wird es, wenn der bei ebay geschossene Plotter leider keine Postscript-Karte enthält und somit nicht in den Workflow integriert werden kann. GunPrinter löst das Problem indem das Programm Tif-Daten zum Drucker sendet. Die Tif-Dateien werden vom Workflow erzeugt und in einen Ordner gespeichert. Diesen Ordner überwacht GunPrinter, sendet die Datei zum Drucker und löscht sie danach wieder. Der Vorteil dabei ist, dass jetzt die Pdfs von dem Rip interpretiert werden, das später auch die Daten für die Druckplatte erzeugt. Fehler sollten also bereits auf dem Plott erkennbar werden da nicht wie in der gängigen Praxis zwei unterschiedliche Rips die Daten interpretieren.

GunPrinter zeigt eine Liste der vorhandenen Dateien an, die ausgewählt und gestartet werden können. Auf Wunsch startet GunPrinter den Druck automatisch. Es können beliebig viele Hotfolder zur Überwachung angegeben werden.

Den Druckvorgang übernimmt eine IrfanView Portable Version, der mit Hilfe einer Batchdatei der Druckauftrag erteilt wird. Das geschieht unsichtbar im Hintergrund. IrfanView verwendet dabei die letzten verwendeten Einstellungen aus seiner ini-Datei. GunPrinter minimiert sich in das System-Tray und prüft alle paar Sekunden die Hotfolder auf neue Daten.

GunPrinter Gui: Beenden nur über „Beenden“

 

Sourcecode:

Für jeden Printjob wird ein neuer GunPrinterThread gestartet. Ein Scanner überwacht dabei die Batch Ausgabe:

public void run() 
    { 

        if (file.exists() && file.canWrite() ) {
                        
            Scanner s = null;
            try {
                ProcessBuilder builder = new ProcessBuilder( "cmd", "/c", "irfan.bat", file.getAbsoluteFile().toString() ); 
                builder.directory( new File(System.getProperty("user.dir")+System.getProperty("file.separator")+"irfan") ); 
                Process p;
                p = builder.start();

                s = new Scanner( p.getInputStream() );
                s.useDelimiter( "\\Z" ); 
                if (s.next().contains("OK")) { //Batch echos OK if ERRORLEVEL = 0
                    oK  = true;                    
                }

            } catch (IOException e1) {
                e1.printStackTrace();
            } finally {
                if (s!=null) s.close();
            }

            if (oK) {
                GunPrinterPanel.protocol(file + " zum Drucker gesendet.");
                
                if (prefs.node("config").getBoolean("deleteOnSuccess", false)) {
                    file.delete();
                    GunPrinterPanel.protocol(file + " gelöscht.");
                }
                else if (!file.getAbsolutePath().toLowerCase().contains("printed")) {
                    String newFileName = file.getAbsolutePath().toLowerCase().replace(".tif","_printed.tif");
                    file.renameTo(new File(newFileName));
                    GunPrinterPanel.protocol(file + " umbenannt zu "+newFileName+" .");
                }
                oK = false;
            }

        }

    }

Interesse:

Frag mich nach einer Kopie des Programms.

Visitenkarten Generator

Wir leben in modernen Zeiten, da bleibt dem Ingenieur kaum Zeit zur Gestaltung einer eigenen Visitenkarte. Oder ein Herr Meir bekommt dreimal die falsche Mayer/Meier/Müller-Karte verpasst.  Daher musste ein Tool her um die Probleme zu lösen!

Setup:

  • php
  • javascript (jQuery)
  • xml-parser
  • pdf Erezugung mit tcpdf
  • Color Klasse überschreiben für CMYK-Unterstützung
  • phpmailer (hier deaktiviert) für Nachricht an Bestellung

Hier könnt ihr damit spielen:

  • als User: Test123 Passwort: Test123
  • als Admin: Admin123 Passwort: Admin123