Saturday, March 5, 2011

Puff Pastry

Finished product - cheese
My first real experience with eating puff pastry came years ago while traveling in Europe. One day while going about my business I happened across a quaint little pastry shop that beckoned me in while it stretched forth invisible permanent tentacles ensnarling my senses.

That day changed me and opened my eyes, nose, taste buds, and pallet to a new way of looking at food.  Before that point I had mostly viewed food as just something you ate to keep you from being hungry, not something that should be enjoyed.  This little shop taught me to start enjoying food in new ways. Food should be good, good for you, but it should also look and smell wonderful and have excellent texture. 
Finished product - jam

So how does puff pastry fit in to all of this? Well puff pastry dough is a light and flaky dough that can be used for both pastries as well as main dishes.  This dough gives a depth of complexity to the flavors and pallet that can not be easily replaced. On top of this, you can do amazing dough art with puff pastry.  So why do more people not cook with it?  On the surface it appears to be difficult and complex to make, however, neither is true. The key to puff pastry is technique and a little bit of patience, plain and simple.

You can do a lot of neat artsy things with puff pastry to make your creations beautiful.  This first post is to work through the basics of the dough and I will be building some simple puff pastry turnovers, nothing ornate just simple wonderful things to eat. 

Now before we get started there are a few key things to always remember.  Heat in the form of room temperature or from friction/viscosity (from working with the dough) is EVIL.  Do not get impatient, this will take several short blocks of time and can be spread over the course of a day or two days.  Follow the technique outlined below; if you try to improvise it will fail. If at all possible work your dough on a marble slab as it will help keep the dough cooler.  When working with the dough and butter, you want to minimize the amount of time it is out of the refrigerator therefore move quick and do not let the dough sit idle. 
Ascorbic Acid powder

Ingredients – Dough
  • 180 ml of cold water
  • 300 grams of sifted all-purpose white flour
  • 4 grams of salt
  • 1/8 teaspoon of ascorbic acid powder
  • 170 grams (~1 and ½ sticks) of cold not frozen unsalted butter
  • Sugar-in-the-raw and cinnamon

Ingredients – Egg Wash
  • 1 egg beaten (umbilical cord removed)
  • 10 ml of cold water

Directions
Dough out of food processor
- Add 180 ml of cold water, sifted flour, salt, and ascorbic acid powder to your food processor to mix.  Make sure you are using the dough blade and have the food processor set for mixing dough. Do not try to use a stand mixer with a dough hook it will not mix it well enough.

Dough ball
- Mix in the food processor for about 15 seconds or so.  Then scrape the bowl and mix for another 30 seconds or until a nice dough ball is formed. 

- Remove the dough and place on a lightly floured stab of marble and knead by hand until you can form a nice round ball.  Wrap the ball in plastic wrap and place in the back of your refrigerator for 30 minutes

Butter sliced
- Take the 170 grams of butter and cut in half length wise.  Arrange these on a piece of plastic wrap so that they form a rectangle.  Cover the butter with plastic wrap and roll out with a rolling pin (using a push down with a ¼ turn method).  (What you are trying to do here is fuse the thinner pieces of butter back together so that they represent a single piece.)

- Place the butter in the back of the refrigerator for 30 minutes or until nice and firm.

- Take dough ball out of the refrigerator and roll out a cross with a “mound” in the middle.

- Place the butter on top of the mound and then fold up the sides of the dough to cover.

Dough cross with mound
Butter all wrapped up
Wrapping butter

- With your rolling pin, push down and roll to form a nice rectangle.  Fold this in thirds, turn the dough 90 degrees, and push down and roll once again in to a nice rectangle.  Fold it back together in thirds, wrap in plastic and put in the back of your refrigerator for at least one hour.

Rolled and ready to be folded
Dough folded - first turn
What you just did was perform one set of two turns out of the three sets that you need to do.  You will be rolling and folding a total of 6 times before you roll out to cut.  Between each set you need to let the dough rest for at least one hour in the refrigerator.

 - After you are finished with all of your turns, cool the dough one last time for an hour.  Then cut in half, put half back in the refrigerator (wrapped in plastic as always) and then roll out the remaining dough so you can cut it in to the desired shapes, in our case large squares.

- Wet the inside edges of each square with a little bit of water, this will help it stick together once you put the pieces on top of each other.  Place your desired filling in the middle and cover. (If you do not plan on using a filling that just use one square, do not double them up without a filling). Use a fork to press and seal the seam and carefully spear the top with the fork to provide a vent.

Sealed
Cut and filled
- Place turnovers on a cookie sheet, brush the tops with the egg wash, and dust with some sugar in the raw (Depending on the filling I also like to sprinkle some cinnamon on top of these simple puff pastry turnovers).

- Bake at 375 degrees for 30 minutes.

Wednesday, February 16, 2011

Update records in Salesforce in mass

Recently I was tasked with a side project to clean up some of our data in Salesforce. The first major task I had was to update thousands of Opportunity records. Given that Salesforce will only allow you to update 100 records at a time I needed to find a way around this. Here is a neat trick to make quick automated work of this problem.

For this example I need to copy the data from a field called Invoice to a field called Accounting. I only want to grab Opportunities where there is data in the Invoice field and I only want to update the Accounting field if there is nothing in it yet. Basically I do not want to clobber existing data.

My example will use APEX code and insert and update triggers to repeat the query over and over.

Step 1:
Create a custom object in SF called updateData (this can be called ANYTHING you want). This object should have 1 number (integer) field called “number”. Technically this is not needed, but my code uses this so I can see how many records were updated. Once you have this created, map it to a Tab so you can find it.

Step 2:
Create an APEX trigger. Yes you will need to use APEX code for this and that means you will need to use the Force IDE editor which is basically just a hacked version of Eclipse. You can NOT create, edit, or do anything with APEX without it. Your trigger should look like this:

trigger updateDate on updateData__c (before insert, before update) {

  if (Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate)) {
    updateData__c[] udata = Trigger.new;

    // Lets grab all of the opportunities that meet our requirements
    // Since we can only update a 100 at a time lets limit our results
    // once again, I only want records where the Invoice field has 
    // something in it and the Accounting field is empty.
    Opportunity[] allOpps = [
      select Id, Name, Accounting__c, Invoice__c
      from Opportunity 
      where Invoice__c <> '' and Accounting__c = ''
      limit 100
    ];

    integer count=0;
    for (Opportunity o:allOpps) {
      // Lets first check to make sure the field is empty, 
      // we do not want to clobber any data.
      if (o.Accounting__c == NULL) {
        o.Accounting__c = Invoice__c;
        update o;
        count++;
      }  
    }
    // This just updates the number field with the 
    // number of records that we actually updated
    for (updateData__c d:udata) {
      d.number__c = count;
    }
  }
}

Step 3:
You will need to create a unit test for this. This unit test just needs to create a single updateData record. So it will look like:

@isTest
private class Test_updateData {

  static testMethod void myUnitTest() {
    updateData__c uData = new updateData__c();
    uData.number__c = 1;
    insert uData;
  }
}

Step 4
At this point you can create a new updateData record and press save. It will update 100 Opportunities. If you edit the record and press save, it will update another 100 records. Now if you are like me and you have thousands of records to do, you need to make use of some web browser automation.

Step 5:
Install the iMacros plugin for Firefox.

Step 6:
Record yourself, with iMacros, editing the record that you created in Step 4. When you are done, you can stop the recording and edit the actual script. It should look something like this (I added the wait seconds=5 at the end to just add an extra delay):

VERSION BUILD=7110203 RECORDER=FX
TAB T=1
URL GOTO=https://xxxxxxx.salesforce.com/yyyyyyyyyyyyyyyyyyy
TAG POS=1 TYPE=INPUT:BUTTON ATTR=NAME:edit&&VALUE:Edit
TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:editPage ATTR=ID:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz CONTENT=
TAG POS=2 TYPE=INPUT:SUBMIT FORM=ID:editPage ATTR=NAME:save&&VALUE:Save
WAIT SECONDS=5

Step 7:
Now set iMacros to play as a loop for as many times as you need and sit back and watch all of your data get updated. NOTE: you can always use the “sForce Explorer” (a Saleforce QUI SQL tool) to query your data and see how many records you will be needing to touch.

Sunday, January 30, 2011

Pineapple Teriyaki Swiss Burgers

Finished Product
This is one of my favorite quick and easy burger recipes. The beautiful thing about these is the fragrance, the presentation, and of course the delicious taste. And no, you do not need to wait for summer to make these. In fact, in a some what cruel and unusual punishment sort of way, I find that the best time to make these is in the middle of January late at night. This way the thermal layers are just right so that the BBQ teriyaki smell gets trapped and the decadent fragrance permeates the entire neighborhood. Talk about messing with the neighbor’s stomachs at 9:00 PM when they are trying to go to bed.

Years upon years ago, I did this for the first time after a late night at work not fully realizing the time. In hind-sight I must have gotten home just after midnight and fired up the grill around 1:00 AM.  By 1:30 AM as I was eating in the quite darkness of the back yard, I noticed kitchen lights flick on in nearly every house in the neighborhood.  Poor things, they were up scouring around their kitchens like little errant mice trying to find something to eat, something to curb their sudden deep thirst for BBQ, without any real knowledge to why they were suddenly so hungry.

Yeah I know it is mean.  But there is something about a nice hot teriyaki burger in the late evening. Now before you get started a few rules of the game.  If you can see through your teriyaki sauce, it is not a sauce but flavored water. If it is not as thick as honey, then it is just flavored water. Now I realize that everyone’s tastes are different, so go hunting for a teriyaki sauce that you like. Try it as a dipping sauce with some chicken or Chinese take out first.  If it is good, then use it else where in your cooking.  Cooking is 30% raw materials, 40% recipe, and 30% technique.  If you do not have good raw ingredients, it will not taste good.

Now one of the readily available commercial sauces I like is the Kikkoman, Terriyaki Sauce from their Takumi Collection.  It has a purplish label and is pretty good, though not wonderful.

Ingredients (2 burgers)

  • 1/2 lb of 80/20 hamburger (no fat, no flavor)
  • 30 ml of Teriyaki sauce (to be mixed in)
  • 30 ml of Teriyaki sauce (to be put on afterwards)
  • 3 grams of Lawry’s seasoned salt
  • 0.5 gram (~1/4 teaspoon) of black pepper
  • 0.5 gram (~1/4 teaspoon) of garlic powder
  • 2 pineapple rings
  • 2 pieces of baby Swiss cheese
  • 2 Kaiser rolls or hamburger buns

Directions

- Knead hamburger, Teriyaki sauce, Lawry’s seasoned salt, black pepper, and garlic together and form in to two patties.

- Barbeque your burgers to your normal desired internal temperature, though I would suggest a nice “medium” that is hot throughout but with a nice slightly pink middle.

- Right before the burgers are done, place a pineapple ring on top of each burger and fill the center of ring with an additional 15 ml of Teriyaki sauce.

- Cover the pineapple with a piece of baby Swiss cheese and let it melt down and seal the pineapple ring to the burger

- Place the Kaiser rolls or hamburger buns on the grill for a few seconds to toast and then serve.

Sunday, January 23, 2011

Honey Wheat Bread

Finished product
I love to eat good bread and recently I have discovered that I actually enjoy making it too. This all started with a falling out I had with my old standby, Grandma Sycamore’s. I used to love Grandma Sycamore's, both their white and their honey wheat.  However, in the past year or so, the quality of the bread has gone down and like so many things the price has gone up by nearly double.

Given this I started my quest to find a good alternative, something that I could start making, and make a hundred or so times to get just right.  I started combing through my baking text books (who knew you could spend nearly 50 pages discussing flour), reading baking blogs, and reading recipes at my favorite recipe site, allrecipes.com. I finally found on allrecipes.com a recipe that looked like it might be a good place for me to start. Obviously I would need to adjust it and tweak to my liking and make adjustments for high altitude.  What I have come up with is the following, enjoy. NOTE: I have tried this in both my 6 quart stand mixer and my 16 cup food processor and have found that it works best in the stand mixer.  Also my stand mixer says to never go above speed 2 for mixing dough.  If yours is different you will need to adjust.

Ingredients – Yeast Start
  • 120 ml warm water
  • 6 grams of white sugar
  • 12 grams of active dry yeast

Ingredients – Bread
  • 1 can (12 fl oz) of evaporated milk
  • 60 ml water
  • 60 ml of honey
  • 60 ml of shortening (Crisco)
  • 12 grams of salt
  • 300 grams of whole wheat flour
  • 500 grams of white bread flour


Directions – Yeast

- Combine the 120 ml of warm water and sugar in small bowl and mix until sugar is dissolved.

- Add yeast and let stand for 10-15 minutes, or until it is foamy 

- Once the yeast is nearly done, start on the bread mixture

    Yeast just added
    Yeast is ready


    Direction – Bread

    - Melt the shortening in your microwave (less than 60 seconds)

    - Combine 60 ml of water, honey, evaporated milk, melted shortening, salt, and the whole wheat flour in your stand mixer bowl (6 quart)

    - Using the paddle blade, mix this on speed 2 for 30 seconds.

    - Scrape the bottom and sides of the bowl and add the yeast mixture and mix for 30 more seconds on speed 2, once again with the paddle blade.  You should have a mixture that resembles pancake batter consistency.

    - Take the paddle blade off and put the dough hook on

    - Let the mixture rest at room temperature for 10-15 minutes.

    - Add 300 grams of the 500 grams of white bread flour and knead for 1 minute 30 seconds on speed 2

    Dough just about done
    - Continue kneading and add 1 heaping tablespoon of the remaining 200 grams of flour every 5 seconds or so until it is all mixed in.  In this step you will knead for about 4 minutes 30 seconds or for a total of 6 minutes with the dough hook.  NOTE:  If you add the remaining 200 grams of flour to quickly it will make a mess. 

    - Remove the dough from the mixer and place on a lightly floured (white bread flour) table.  Lightly knead by hand (folding out side to center and turning method) for 30 seconds and then make in to a dough ball, spinning the dough in your hands to seal the bottom. 

    Rolled out dough
    - Place dough ball in to a large buttered bowl and lightly cover with plastic warp (do not seal it closed) and set aside in a warm place to rise.  It can take an hour or two or three to rise.

    - Once the dough has visibly doubled, remove from bowl and place on lightly floured table (white bread flour) and cut in half or thirds depending on pan size (see below in cooking time section, large pans cut in half, medium sized pans cut in thirds).  Separate the halves and roll out to the width of your bread pans. 

    Start of second rise
    - Roll the dough in to a log and place in a buttered bread pan with the seam side down.  Lightly cover with plastic wrap and let rise again (the second rise).  This rise usually takes less time. (At this point you could wrap the dough log in plastic wrap and freeze instead of putting it in to the bread pan for the second rise.  If you do this you can then treat it like any other frozen bread dough at a later time)

    Out of the oven
    - Once the dough has visibly doubled and filled the pan you can remove the plastic cover, cut the top, and bake it.

    - Bake at 375 degrees for 30 minutes for a large pan (5.5 inch x 9.5 inch x 2.5 inch pan) or 22 minutes for a medium pan (4.5 inch x 8.5 inch x 2.5 inch). I prefer and recommend the large pans.

    - When time is up, remove immediately from pan and place on a cooling rack to cool.  After an hour or so you can put the bread in a bread bag to store. 

      Wednesday, January 19, 2011

      Life is to short to eat crappy food!

      A friend of mine once told me "life is to short to eat crappy food", and I must say I completely agree with him.  I am constantly amazed at how often we as society eat things that are just down right disgusting.  We make attempts to justify the crap we eat and make ourselves feel better by saying it was cheap or it was fast or...?  But it does not matter, it is still crappy food and often not very good for us.

      I wonder, are we as society just apathetic when it comes to what we eat, have we been socialized in to thinking certain foods are good or good for us, or are we just down right lazy and unwilling to either make or eat things that are good?  My guess is that it is a mix of the three.  From my research the general decline in food and the acceptance of crappy food started with the commercial brain washing of the 1950, 60s and 70s.  Do you remember when margarine came with a packet of nuclear yellow dye?  It was sold to us that this was the way of the future.  I also loved the push for "TV Dinners" that were definitely a ploy to get us to spend more time out of the kitchen and in front of the television watching mindless entertainment and making ourselves fat.  When you honestly think about it, none of these prepackaged food back then were good and they surely were not good for us. But we as society were hoodwinked by marketing and the commercialization of "the future" to start eating crappy food.

      Today, I believe that too many people have forgotten or never learned how to cook.  Basic kitchen and cooking techniques are not being learned in the home.  Most recipes, if you can call them that, call for a can of this or a box of that as the main ingredient.  Just as we need to refocus on the family and fix that basic building block, we need to fix what it is we eat and serve in our homes.  People need to really consider what it is they are eating. 

      Over the years I have developed a love of eating and in turn cooking, baking and candy making.  Through this blog I hope to share with you some of my successes and failures I have with the places I eat at and the things I make.  I also hope I can convince you of a simple fact that it is not hard or expensive to eat and cook good food.

      Please note, I am not a tree hugging soil munching druid so I will shop at stores other than Whole Foods (though I love Whole Foods), and yes, I will eat meat with the best of them.  I also have one general snobby rule in my kitchen, it states that if the recipe calls for a can of Campbells anything to be mixed in, that recipe is forthwith given a burning at the stake that would make the Salem witch hunts jealous.

      I hope you enjoy my random posts and find my recipes fun to make.  Please feel free to comment on the recipes with your variations.  Just so you know, all of my recipes are tailored for an elevation of ~1370 meters (4500 feet) above sea level and for a very low humidity climate.  I only use a natural gas range and the oven is NON convection so if your oven is electric, convection, or both, your baking times will be different.

      Friday, October 29, 2010

      Upstart, a better init or a more painful one

      For some time now Upstart has slowly been taking over the old SysV init scripts on most Linux distributions, for better or worse. Now some of the things they are trying to do with Upstart are good and have some merit, though at the cost of simplicity and ease of use. I fear the developers and supporters are worrying more about the 5% use case rather than the 95% use case and they have thrown the old mantra of keep it simple and stupid out the window. The problem when you give the 5% too much focus, you end up creating a solution that is horribly broken and resembles solutions from the little company up in Redmond Washington.

      For those struggling to find your way through the new hell of Upstart here are a few pointers:

      1. Dependency hell is brought to a whole new level of pain and frustration. Imagine a system where everything can emit and event and things “should” start or stop based on those events. Hey that is a great theory. But how on earth do you troubleshoot it. How do you get your system to start things in an exact order without building a chain like what we had back with SysV init scripts. In the old days of SysV, you gave all of your scripts a number, and they started in order from lowest to highest. If you wanted something to start sooner, you just lowered the number. Simple. Now you have to try and go through and figure out what starts when and what needs to happen before everything else. There is no tool that will print out a dependency map and there is no logging or debugging to speak of.

      2. The location of Upstart configuration files can be found at /etc/init/. The old remaining SysV init scripts that can not yet be converted to Upstart can be found at /etc/init.d/.

      3. The scripting support in the “script” blocks of Upstart is very limited and extremely basic. Basic things like echoing out data to the console does not work which makes it hard to try and use the scripts for anything other than shotgun starting a service or kill-9ing it.

      4. The default answer to stopping a daemon in Upstart is to sent it a SIGTERM and wait a few seconds and if it is not stopped to send it a SIGKILL, a kill -9. Holy crap batman, there goes my database server that takes a while to shutdown cleanly.

      5. You can not create any custom function blocks to pull extra status or details from a server that might support it. So your only option is to create a separate tool to manage the daemon. If you are going to do that, why not just stay with the SysV init scripts.

      6. Most Linux Distributions are pushing this down our throats fast and hard, even when it is not fully baked or fully functional yet. This reminds me of the tactics that come from a little company in Redmond Washington.

      7. Upstart can not manage all services yet, and some just do not start at all with Upstart. In the effort to build the most advanced leaky abstraction, some times a daemon will fork once or twice and Upstart can not track it and thus you get in to a state where Upstart “thinks” the service is stopped or started when it really has not. So imagine if that was your database server again, it would suck if the system went ahead and shutdown the filesystem even though the database server is not even close to shutting down.

      I have spoken with the lead developer of Upstart several times. And he seems reasonable and desires to fix at least some of the problems and weaknesses in Upstart. However, I fear that there are some that he will not fix. Some of the things that we need it to do are not in the vision of it.

      The things I would change about Upstart, sooner rather than later are:

      1) Make the scripting engine inside the script blocks work with all script commands and be a fully functional scripting engine.

      2) Provide a tool that prints out the exact order that things will start at boot with a parenthetical list next to each entry with its dependencies.

      3) Provide the ability to add arbitrary functions/scripts to the Upstart configuration files so you can do something like “initctl apache super-details” and have it run a script block inside the configuration file called “super-details” which does what ever you need it to do.


      I hope that Upstart and other proposed replacements to SysV init scripts can focus on making a better solution and not focus on trying to solve a 5% use case problem that causes pain and frustration for the other 95%.

      Wednesday, October 6, 2010

      PDF Document Manipulation

      With the advent of advanced mobile reading devices that support full graphic displays, such as the iPad and soon to be released Android based tablets, we in the network security space find ourselves wanting to bring our entire reference library of books, ISO Standards, RFC, and diagrams with us everywhere we go without the concern of do we have internet connectivity and do we remember where all of the documents are without having to dig around the Internet.

      The problem, however, is that a lot of the reference documents that we use are published on-line in text or PDF format and are only available in pieces (chapter by chapter).  This was probably done to save bandwidth and for ease of downloading and viewing.

      This type of on-line publishing can work well when using a full sized computer that has disk storage or a real file system and keyboard/mouse.  This does not work well for mobile readers that do not have disk storage file system to store hundreds of separate files and may be used in places where you do not have internet access (on a plane over the Atlantic or in that basement corner conference room that is just out of wifi range).

      We are going to address how we can manipulate on-line PDF documents that are published in parts to turn them in to one PDF “book” for easy of use on a mobile reader.  We will also discussion in Example 2 how to fix bookmarks that no longer point to the right place due to adding or taking pages away. 

      Example 1:
      We have found a book online that has 4 separate chapters that we would like to make a single PDF “book” out of for our mobile reader.  Each chapter is a separate download called chapter1.pdf, chapter2.pdf, etc.  The first page of each PDF is actually a blank page and the second page is note/summary/title page, neither of these pages we want to keep.

      Step 1.1:
      Download all related files to your computer. You will need to have local copies of all the files for the book or standard document in question.  So lets download them to say /home/pdf/

      Step 1.2:
      Remove any leading or artifact pages that are not relevant to the combined book.  Some times you will find that PDF documents have a leading blank page, or leader “summary” page, or master title page for every chapter.  When this is not desirable, we can easily remove them prior to stitching the chapters together.  (Be mindful of any copyright or usage rules when making changes).  The tool we will be using is called “pdftk”.

      [jordan]:/home/pdf-> pdftk chapter1.pdf cat 3-end output new-chap1.pdf

      What this command will do is take the current PDF of “chapter1.pdf” and create a new PDF document called “new-chap1.pdf” but it will skip the first two pages.  It will start on page 3 and go to the end of the PDF document.  Lets do this for the other 3 chapters as well.


      [jordan]:/home/pdf-> pdftk chapter2.pdf cat 3-end output new-chap2.pdf
      [jordan]:/home/pdf-> pdftk chapter3.pdf cat 3-end output new-chap3.pdf
      [jordan]:/home/pdf-> pdftk chapter4.pdf cat 3-end output new-chap4.pdf


      Step 1.3:
      Now that we have removed all of the leading white pages and title pages, let us combine the 4 chapters in to one PDF document called “book.pdf”


      [jordan]:/home/pdf-> pdftk new-chap1.pdf new-chap2.pdf new-chap3.pdf new-chap4.pdf cat book.pdf


      Step 1.4:
      Now that we have a single PDF document that has all the chapters stitched together, we can now look in to adding PDF bookmarks to the file to make jumping around easier on our mobile reader.  This can easily be done with a tool called “jpdfbookmarks” that you can download here: http://sourceforge.net/projects/jpdfbookmarks/.  As of this writing I am using version 2.4.1.  From this tool you can add bookmarks and sub-bookmarks very easily from their graphical interface.  We will create a bookmark for Chapter 1, Chapter 2, etc, and we will also create sub-bookmarks called Chapter 1.1, Chapter 1.2, etc for all of the sub elements in the chapter. Once you are done save your changes.


      Example 2:
      We have a PDF document that we either created in Example 1 or that we downloaded from the Internet that we would like to add a title page or a picture page (picture of the cover of the book) to the front of the PDF document.  Now ideally if this is from Example I, we would have done this during Step 1.3 so as not to mess up all of our bookmarks.  If we try to just use the pdftk command by itself, all of our bookmarks will be off by the number of pages that we insert.  This method would also work if we needed to remove a page and wanted to keep all of our bookmarks.  So what we can do is the following:

      Step 2.1:
      Create our title page as a PDF document or convert the PNG/JPG picture of the book to a PDF document and call it “cover.pdf”.

      Step 2.2:
      Lets add the “cover.pdf” to the first of the “book.pdf” document.  NOTE: when we do this all of the bookmarks will be off by the number of pages that we insert, but I will show you how to fix this. For the sake of explanation, let assume that the “cover.pdf” is only 1 page long.


      [jordan]:/home/pdf-> pdftk cover.pdf book.pdf cat output bookwithcover.pdf


      This will create a new PDF document called “bookwithcover.pdf” that will have the new title page or picture cover added to the front of the book.  For our example, all of the bookmarks will now be off by one page.

      Step 2.3
      First we need to export our current bookmarks so we can fix all of them in mass.  If we use the “jpdfbookmarks” tool that we used up in Step 1.4 we can “dump”/export all of the current bookmarks for the “bookwithcover.pdf” document to a text file.  This file will look like:


      Chapter 1 - BookmarkNameAAA/1,Black,notBold,notItalic,closed,FitPage
          1.1 BookmarkNameBBB/1,Black,notBold,notItalic,open,FitPage
          1.2 BookmarkNameCCC/2,Black,notBold,notItalic,open,FitPage
      Chapter 2 - BookmarkNameDDD/3,Black,notBold,notItalic,closed,FitPage
          1.1 BookmarkNameEEE/3,Black,notBold,notItalic,open,FitPage
          1.2 BookmarkNameFFF/4,Black,notBold,notItalic,open,FitPage
      etc….


      The “BookmarkName???” is just the name that you gave the bookmark with you created it and is what shows up in the bookmarks pane in your PDF viewer.  The slash “/” # that follows the name is the page in the PDF document that it points to. 

      Step 2.4
      We can now use a short piece of PERL to go in and fix all of the bookmarks in mass.  This assumes that you saved the bookmarks as “dump.txt” and the changes will be saved to “dump1.txt”.  We will also be changing all of the bookmarks by one page since we only added one page.  NOTE: this is written in long hand PERL for readability.


      #!/usr/bin/perl

      $file="dump.txt";
      open (DATAIN, $file);
      @fileline = ;
      close (DATAIN);

      $file1="dump1.txt";
      open (DATAOUT, ">$file1");

      foreach (@fileline)
      {
          m/\/(.{1,3})\,/;

          # This is where we increase it by 1.
          $newpage = $1 + 1;
          s/\/.{1,3}\,/\/$newpage\,/;
          print DATAOUT "$_";
      }
      close (DATAOUT);


      Step 2.5
      Now that we have a file called “dump1.txt” that has all of the corrected bookmarks, we need to import that back into our “bookwithcover.pdf” document.  Let’s use the “jpdfbookmarks” tool once again to do this for us.  Open the PDF document with jpdfbookmarks and then use the “Load” function in the “Tools” menu to load “dump1.txt” which is the new bookmarks.  Save the PDF and you are done.