Today, Quartam Software is proud to announce the release of Quartam PDF Library for LiveCode version 1.1, as open source under a dual license. Quartam PDF Library allows LiveCode developers to go beyond 'print to pdf file' as it offers pin-point control, extensive graphics support and much more.
Where does Quartam PDF Library come from?
Back in December 2005, I started Quartam PDF Library as a research project for adding PDF export to Quartam Reports. It was spun off as a separate commercial product and used in a wide variety of LiveCode-based projects, such as BlueMango's ScreenSteps.
Although I had kept working on new features, more pressing matters (like my day-job) kept me from pushing ahead and wrapping up a new release. The advent of 'print to pdf' features in LiveCode 4.5 triggered a soul search and eventually led to the decision to release the new version as open source.
The end result: nearly 7300 lines of production quality code, ready for you to use in your LiveCode projects.
So what is new in version 1.1?
The code got a good cleanup, complies with the rules of variable checking, and replaces most string literals with constants to prevent bugs.
Plus, the following features were added:
- Transformations (scale, translate, rotate, skew, mirror)
- Transparency and blendmodes
- Gradients
- Clipping
- Text box fitting
- Inserting pages (ideal for building a table of contents with bookmarks)
- Compression
- Experimental support for including EPS files (Emulated PostScript)
And for the first time, we offer support for generating PDF documents in LiveCode Server scripts, including On-Rev!
Quartam PDF Library now requires Revolution 3.0 or later, with LiveCode 4.6 highly recommended.
How do you mean: open source under a dual license?
Although Quartam PDF Library is a free/open source software (F/OSS) project, giving you a lot of freedom and flexibility as to how you use it in your own projects, this doesn't mean you're free to do anything you want with it: you have to respect the GNU Affero General Public License (AGPL).
You can be released from the requirements of the AGPL license by purchasing a commercial license from Quartam Software.
Buying such a license is mandatory as soon as you develop commercial activities involving Quartam PDF Library without disclosing the source code of your own applications. These activities include: offering paid services to customers as an ASP, serving PDF documents generated dynamically in a web application, shipping Quartam PDF Library with a closed source product.
Such a commercial license releases you from the requirements of the copyleft AGPL license, which include: distribution of all source code, including your own product; licensing of your own product under the AGPL license; prominent mention of the Quartam copyright and the AGPL license; and disclosure of modifications to the library.
In addition, the commercial license releases you from the requirement not to change the PDF Producer line in the generated PDF document properties.
What about my previous commercial license for Quartam PDF Library version 1.0?
Of course you can keep using the closed source version 1.0 in your projects. And if your project is open source, you can use version 1.1 without an additional charge.
However, if you want to use version 1.1 in a commercial activity, you have to purchase an upgrade for USD 49 from the Quartam Software Online Store.
And if you never bought a copy of Quartam PDF Library, you can purchase the commercial license for USD 149 from the Quartam Software Online Store or the LiveCode Marketplace.
How can I contribute to the Quartam PDF Library project?
I'm glad you asked - the plan is to build a community around Quartam PDF Library in order to streamline the development of newer versions. If you can help with squashing bugs, researching new features, improving documentation, or any other way, you're more than welcome to join us.
All you need to do is download, sign and email back the Quartam Open Source Contributor Agreement so that your contributions can be incorporated into the project. Quartam Software has the role of project custodian, taking care of versioning and distribution.
One such contribution was made by John Craig (Splash21) to add compression support to the library, which is included in Quartam PDF Library version 1.1 - another contribution was made by Trevor DeVore (BlueMango) who offered code for writing LiveCode htmlText to a PDF document, which I have yet to integrate but looks really promising.
And I have some experimental code that I'd love to share and put into the project after review - so any reports of this library's death were greatly exaggerated.
So roll up your sleeves, download the new version and get stuck in!
Showing posts with label PDF. Show all posts
Showing posts with label PDF. Show all posts
Thursday, April 21, 2011
Thursday, December 30, 2010
Stamping PDF files
In a previous post, I examined how we can use LiveCode and the Java-based iText library to concatenate a series of existing PDF files into a single PDF file. Now we will examine how we can 'stamp' a PDF file with an image using the same technique.
The first thing to code is the Java class that we will call using the shell function. Here's what I came up with:
In a nutshell, the first parameter is the input file, the second the output file, the third the image file, and the fourth parameter is a comma-separated list of coordinates making up the target rectangle. As usual, the code is a tad lazy when it comes to faulty input parameters and exception handling - if there's a mistake you'll simply get the stacktrace as the output of the shell command.
The most important bit is in the loop over the pages, where we use the outputStamper.getOverContent() method to draw our image on top of the existing content. If you'd rather have the image in the back, as a watermark, you would use the outputStamper.getUnderContent() methopd instead. Also note that setting the image position coordinate system works from the bottomLeft of the page, so we have to use the original page height and subtract the bottom coordinate from it.
Now we can proceed with writing a LiveCode button script:
Click the button, and it happily takes the existing PDF files (demo1.pdf), paints the image (Template.png) on top of all pages, and writes a new PDF file (stamp.pdf) in the same folder as our stack. There we have it, another example of using iText from within LiveCode.
The first thing to code is the Java class that we will call using the shell function. Here's what I came up with:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
public class StampPdfFile {
public static void main(String[] args) throws IOException, DocumentException {
final String inputFile = args[0];
final String outputFile = args[1];
final String imageFile = args[2];
final String[] coords = args[3].split(",");
final PdfReader inputReader = new PdfReader(inputFile);
final OutputStream outputStream = new FileOutputStream(outputFile);
final PdfStamper outputStamper = new PdfStamper(inputReader, outputStream);
final int pageCount = inputReader.getNumberOfPages();
final Image image = Image.getInstance(imageFile);
final int left = Integer.parseInt(coords[0]);
final int top = Integer.parseInt(coords[1]);
final int right = Integer.parseInt(coords[2]);
final int bottom = Integer.parseInt(coords[3]);
final int height = bottom - top;
final int width = right - left;
image.scaleToFit(width, height);
for (int pageIndex = 1; pageIndex <= pageCount; pageIndex++) {
final PdfContentByte overContent = outputStamper.getOverContent(pageIndex);
final Rectangle pageSize = inputReader.getPageSize(pageIndex);
image.setAbsolutePosition(left, pageSize.getHeight() - bottom);
overContent.addImage(image);
}
outputStamper.close();
}
}
In a nutshell, the first parameter is the input file, the second the output file, the third the image file, and the fourth parameter is a comma-separated list of coordinates making up the target rectangle. As usual, the code is a tad lazy when it comes to faulty input parameters and exception handling - if there's a mistake you'll simply get the stacktrace as the output of the shell command.
The most important bit is in the loop over the pages, where we use the outputStamper.getOverContent() method to draw our image on top of the existing content. If you'd rather have the image in the back, as a watermark, you would use the outputStamper.getUnderContent() methopd instead. Also note that setting the image position coordinate system works from the bottomLeft of the page, so we have to use the original page height and subtract the bottom coordinate from it.
Now we can proceed with writing a LiveCode button script:
on mouseUp
--> determine the input, output and image files
local tInputFile, tOutputFile, tImageFile
put ShellPath(AbsolutePathFromStack("demo1.pdf")) \
into tInputFile
put ShellPath(AbsolutePathFromStack("stamp.pdf")) \
into tOutputFile
put ShellPath(AbsolutePathFromStack("Template.png")) \
into tImageFile
--> determine the image target rectangle
local tImageRect
put quote & "10,10,103,87" & quote \
into tImageRect
--> determine the class path
local tClassPath
if the platform is "Win32" then
put ".;iText-2.1.7.jar" into tClassPath
else
put ".:iText-2.1.7.jar" into tClassPath
end if
--> assemble the shell command
local tShellCommand
put "java -classpath" && tClassPath && \
"StampPdfFile" && \
tInputFile && \
tOutputFile && \
tImageFile && \
tImageRect \
into tShellCommand
--> execute the shell command
local tHideConsoleWindows, tDefaultFolder, tShellResult
put the hideConsoleWindows into tHideConsoleWindows
set the hideConsoleWindows to true
put the defaultFolder into tDefaultFolder
set the defaultFolder to AbsolutePathFromStack()
put shell(tShellCommand) into tShellResult
set the defaultFolder to tDefaultFolder
set the hideConsoleWindows to tHideConsoleWindows
if tShellResult is not empty then
answer error tShellResult
end if
end mouseUp
function AbsolutePathFromStack pFileName
local tAbsolutePath
put the effective filename of this stack into tAbsolutePath
set the itemDelimiter to slash
if pFileName is not empty then
put pFileName into item -1 of tAbsolutePath
else
delete item -1 of tAbsolutePath
end if
return tAbsolutePath
end AbsolutePathFromStack
function ShellPath pPath
if the platform is "Win32" then
put quote & pPath & quote into pPath
else
replace space with backslash & space in pPath
end if
return pPath
end ShellPath
Click the button, and it happily takes the existing PDF files (demo1.pdf), paints the image (Template.png) on top of all pages, and writes a new PDF file (stamp.pdf) in the same folder as our stack. There we have it, another example of using iText from within LiveCode.
Wednesday, December 29, 2010
Concatenating PDF files
Since the advent of LiveCode 4.5, developers have the ability to 'print' stack content directly to PDF files. And if you need pin-point control over what goes where, you can use Quartam PDF Library to generate PDF files from scripts. That's great if you are in full control of the content, but what if you need to work with existing PDF files? In the next few posts, we will examine how you can tap into the power of the Java-based iText library from LiveCode.
So let's start by downloading a copy of iText version 2.1.7 - do not use version 5.x as the API changed and the following example code won't work.
The first question is: how can we execute Java code from LiveCode? The simplest solution is the shell function: it allows you to execute DOS or Unix commands, as if you typed them in from the command line. Note that on Windows, using this function will show a DOS window, but you can control that by setting the hideConsoleWindows property before calling the shell function.
You can test it out by simply executing the following line from the message box:
The second question is: what sort of Java code do we need to write? Well, I fired up a copy of Eclipse, started a new project, and created a new class 'ConcatPdfFiles' in the default package. Then I grabbed my paper copy of iText in action (first edition) and flipped to page 64 as this contains the examples for concatenating PDF files. A little bit of thinking, and I derived the following code:
As you can see, the code is a bit lazy when it comes to exception handling: I just let the exceptions get thrown, and this will be the output of our shell call if something goes wrong. Note also that the first argument is the output file, followed by the input files that you want to concatenate into the output file.
More importantly, at this point in time, the code doesn't compile. The problem is, we haven't yet told Eclipse where that iText-2.1.7.jar library file is, so compilation fails. This is sometimes referred to as 'classpath hell' - you have to give Java a list of paths where it can find the necessary additional libraries, not just at compile time but also at runtime as we'll see later.
Because I like to keep everything together in my Java projects, I added a new 'lib' folder to my project, and copied the iText2.1.7.jar file into it. At that point, you can use the contextual menu on the iText.2.1.7.jar file, and add it to the Build Path. Now the code I showed earlier compiles just fine, and we can proceed to the next stage.
The third question is: how do we put everything together in LiveCode? We'll begin by putting all the necessary parts into a single folder: the iText-2.1.7.jar library file, the ConcatPdfFiles.class compiled file and two example PDF files (demo1.pdf and demo2.pdf). Then we fire up LiveCode, create a new stack 'ConcatPdfFiles' and save it in the same folder as the other files, naming it "ConcatPdfFiles.liveCode'. Now we can drop a button onto the stack and start scripting.
Now we need to determine the correct command to be executed by the shell function. It should look something like:
The java executable needs the correct classpath, and we need to pass in compatible file paths.
Let's start with the classpath. This is a list of places that java needs to look for its .class files - as separate files in folders, or stored together in a .jar file. And for extra fun, the separator character is a colon on Unix-based platforms, and a semicolon on Windows. You can have relative paths in this classpath, and '.' (period) is short for the current directory. So rather than building a long class path, we can circumvent the issue by setting the defaultFolder property to change the working directory before calling the shell function. Then our classpath can be as short as:
The next bit is compatible file paths. The good news: LiveCode uses a '/' (slash) as separator, regardless of the underlying platform, and Java is more than happy to accept '/' in a path, even when it's running on Windows. However, if there are spaces in the path, we need to save them by putting quotes around the path on Windows, and escaping the spaces with a backslash on Unix-based platforms.
And to determine the paths relative to the stack's location on your hard disk, we'll need a helper function that uses the effective filename property of our stack.
So finally, we have a button script as follows:
Click the button, and it happily concatenates the two PDF files (demo1.pdf and demo2.pdf) into a single PDF file (output.pdf) in the same folder as our stack. There we have it, our first use of iText from within LiveCode.
So let's start by downloading a copy of iText version 2.1.7 - do not use version 5.x as the API changed and the following example code won't work.
The first question is: how can we execute Java code from LiveCode? The simplest solution is the shell function: it allows you to execute DOS or Unix commands, as if you typed them in from the command line. Note that on Windows, using this function will show a DOS window, but you can control that by setting the hideConsoleWindows property before calling the shell function.
You can test it out by simply executing the following line from the message box:
answer shell("java -version")
The second question is: what sort of Java code do we need to write? Well, I fired up a copy of Eclipse, started a new project, and created a new class 'ConcatPdfFiles' in the default package. Then I grabbed my paper copy of iText in action (first edition) and flipped to page 64 as this contains the examples for concatenating PDF files. A little bit of thinking, and I derived the following code:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfReader;
public class ConcatPdfFiles {
public static void main(String[] args) throws DocumentException, IOException {
final String outputFilePath = args[0];
final OutputStream outputStream = new FileOutputStream(outputFilePath);
final Document outputDocument = new Document();
final PdfCopy outputCopy = new PdfCopy(outputDocument, outputStream);
outputDocument.open();
for (int i = 1; i < args.length; i++) {
final PdfReader inputPdfReader = new PdfReader(args[i]);
final int pageCount = inputPdfReader.getNumberOfPages();
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
outputCopy.addPage(outputCopy.getImportedPage(inputPdfReader, pageIndex + 1));
}
}
outputDocument.close();
}
}
As you can see, the code is a bit lazy when it comes to exception handling: I just let the exceptions get thrown, and this will be the output of our shell call if something goes wrong. Note also that the first argument is the output file, followed by the input files that you want to concatenate into the output file.
More importantly, at this point in time, the code doesn't compile. The problem is, we haven't yet told Eclipse where that iText-2.1.7.jar library file is, so compilation fails. This is sometimes referred to as 'classpath hell' - you have to give Java a list of paths where it can find the necessary additional libraries, not just at compile time but also at runtime as we'll see later.
Because I like to keep everything together in my Java projects, I added a new 'lib' folder to my project, and copied the iText2.1.7.jar file into it. At that point, you can use the contextual menu on the iText.2.1.7.jar file, and add it to the Build Path. Now the code I showed earlier compiles just fine, and we can proceed to the next stage.
The third question is: how do we put everything together in LiveCode? We'll begin by putting all the necessary parts into a single folder: the iText-2.1.7.jar library file, the ConcatPdfFiles.class compiled file and two example PDF files (demo1.pdf and demo2.pdf). Then we fire up LiveCode, create a new stack 'ConcatPdfFiles' and save it in the same folder as the other files, naming it "ConcatPdfFiles.liveCode'. Now we can drop a button onto the stack and start scripting.
Now we need to determine the correct command to be executed by the shell function. It should look something like:
java -classpath <class-path> ConcatPdfFiles <output-file> <input-file-1> <input-file-2> ...
The java executable needs the correct classpath, and we need to pass in compatible file paths.
Let's start with the classpath. This is a list of places that java needs to look for its .class files - as separate files in folders, or stored together in a .jar file. And for extra fun, the separator character is a colon on Unix-based platforms, and a semicolon on Windows. You can have relative paths in this classpath, and '.' (period) is short for the current directory. So rather than building a long class path, we can circumvent the issue by setting the defaultFolder property to change the working directory before calling the shell function. Then our classpath can be as short as:
.:iText-2.1.7.jaron MacOS X/Linux and
.;iText-2.1.7.jaron Windows.
The next bit is compatible file paths. The good news: LiveCode uses a '/' (slash) as separator, regardless of the underlying platform, and Java is more than happy to accept '/' in a path, even when it's running on Windows. However, if there are spaces in the path, we need to save them by putting quotes around the path on Windows, and escaping the spaces with a backslash on Unix-based platforms.
And to determine the paths relative to the stack's location on your hard disk, we'll need a helper function that uses the effective filename property of our stack.
So finally, we have a button script as follows:
on mouseUp
--> determine the input and output files
local tInputFiles, tOutputFile
put ShellPath(AbsolutePathFromStack("demo1.pdf")) && \
ShellPath(AbsolutePathFromStack("demo2.pdf")) \
into tInputFiles
put ShellPath(AbsolutePathFromStack("output.pdf")) \
into tOutputFile
--> determine the class path
local tClassPath
if the platform is "Win32" then
put ".;iText-2.1.7.jar" into tClassPath
else
put ".:iText-2.1.7.jar" into tClassPath
end if
--> assemble the shell command
local tShellCommand
put "java -classpath" && tClassPath && \
"ConcatPdfFiles" && \
tOutputFile && tInputFiles \
into tShellCommand
--> execute the shell command
local tHideConsoleWindows, tDefaultFolder, tShellResult
put the hideConsoleWindows into tHideConsoleWindows
set the hideConsoleWindows to true
put the defaultFolder into tDefaultFolder
set the defaultFolder to AbsolutePathFromStack()
put shell(tShellCommand) into tShellResult
set the defaultFolder to tDefaultFolder
set the hideConsoleWindows to tHideConsoleWindows
if tShellResult is not empty then
answer error tShellResult
end if
end mouseUp
function AbsolutePathFromStack pFileName
local tAbsolutePath
put the effective filename of this stack into tAbsolutePath
set the itemDelimiter to slash
if pFileName is not empty then
put pFileName into item -1 of tAbsolutePath
else
delete item -1 of tAbsolutePath
end if
return tAbsolutePath
end AbsolutePathFromStack
function ShellPath pPath
if the platform is "Win32" then
put quote & pPath & quote into pPath
else
replace space with backslash & space in pPath
end if
return pPath
end ShellPath
Click the button, and it happily concatenates the two PDF files (demo1.pdf and demo2.pdf) into a single PDF file (output.pdf) in the same folder as our stack. There we have it, our first use of iText from within LiveCode.
Sunday, November 21, 2010
I went to the Devoxx 2010 conference and...
and I got the t-shirt to prove it

100% Javaholic was the motto of this year's Java developer conference, organised by the Belkgian JUG and held in Antwerp.
Of course, I wasn't there to collect the t-shirt - unlike some others who shall remain unnamed and went from booth to booth to fetch a t-shirt, even if they weren't interested in the product. You know who you are :-p
and I met Bruno Lowagie
For those who don't know Bruno Lowagie, he's the primary developer behind the iText project, an open-source library for producing PDF files using Java. There must be something in the water around here, as he lives about 30 miles from my house - two Belgians, both writing PDF libraries - what are the odds?
As he was busy signing copies of iText in Action (second edition) the conversation was rather short, but he signed my copy of his book with "To a fellow PDF developer" - what a compliment :-)
and I managed to squeeze LiveCode onto a whiteboard

Given that someone actually added 'HyperTalk :-)' to the board right behind my entry, I think I wasn't the only guy there who knows LiveCode. Next time, poke me and I'll buy you a drink.
and had a very good time overall
Plenty of interesting sessions, friendly environment and excellent organization. I'll definitely go back next time I get the chance.

100% Javaholic was the motto of this year's Java developer conference, organised by the Belkgian JUG and held in Antwerp.
Of course, I wasn't there to collect the t-shirt - unlike some others who shall remain unnamed and went from booth to booth to fetch a t-shirt, even if they weren't interested in the product. You know who you are :-p
and I met Bruno Lowagie
For those who don't know Bruno Lowagie, he's the primary developer behind the iText project, an open-source library for producing PDF files using Java. There must be something in the water around here, as he lives about 30 miles from my house - two Belgians, both writing PDF libraries - what are the odds?
As he was busy signing copies of iText in Action (second edition) the conversation was rather short, but he signed my copy of his book with "To a fellow PDF developer" - what a compliment :-)
and I managed to squeeze LiveCode onto a whiteboard

Given that someone actually added 'HyperTalk :-)' to the board right behind my entry, I think I wasn't the only guy there who knows LiveCode. Next time, poke me and I'll buy you a drink.
and had a very good time overall
Plenty of interesting sessions, friendly environment and excellent organization. I'll definitely go back next time I get the chance.
Wednesday, May 14, 2008
Revolution Live '08 - redefining the web
Now that I'm back home, caught up on sleep a bit and am starting the happy-fun return of my internal body clock to the Central European Time Zone, it seemed like a good opportunity to blog about the announcements at the Revolution Live Conference.
The great news for Revolution customers around the globe, is that their web offerings are shaping up really well: a PHP-style server module for creating web applications, plus a web browser plug-in - talk about a slam-dunk by the Runrev team!
This is the perfect answer to Adobe's trying to bring Flash to the desktop: anyone can now build Internet-enabled applications without having to struggle with ActionScript and tools that were meant for designers. Not that designers are illogical people - if they were clueless about putting two and two together, they wouldn't be able to pull off that AJAX stuff.
Boy, am I glad I won't have to continue struggling with that HTML+CSS+JavaScript batter mix either, if I want to build a Rich Internet Application... Let's check how Revolution stacks up against all these technologies that are trying to win the next round of browser wars:
- AJAX is an interesting use of existing technologies, but in the end it's just another way to stretch what a browser can do. The fact of the matter is: the browser needs updating for it to ever become a true universal application platform.
- Flash started life as a way to animate vector graphics, and whatever people may tell you, that's still what it is - they don't even have real buttons, it's all simulated. Thank you, please don't come again.
- JavaFX is an interesting idea, but suffers from one major flaw: it is not Java. Which means that Java developers have to learn a whole new thing just to be with the times. If they really wanted it to become popular, they would have just provided a great applet builder, not this monstrosity.
- Silverlight is Microsoft's attempt to beat Adobe in the browser plug-in wars. So far, it doesn't really seem to be getting very far. Maybe around version 3.0 we can consider it a major force, but I'm sure that you'll have to update your Silverlight apps every single time the Redmond team decides to replace essential parts.
Some people may think that Revolution is focusing entirely on the multimedia market with this strategy, but they couldn't be further from the truth: this is the single largest opportunity in the history of Revolution to make it big in the world of Enterprise applications!
Of course, there will be plenty of multimedia Flash-like stuff built using this browser plug-in, and it should attract a whole new crowd of Revolution developers. But looking at the biz app developers now, Revolution is by far the most secure technology of the bunch: short of obfuscating your Javascript, your AJAX apps are wide-open for anyone to take apart and try to take advantage of.
The Revolution community can now deploy applications for those customers who want to see a browser user interface, and we won't even have to learn a new language. Likewise, the server module will serve as an application server gateway like we haven't had before.
And I can assure you that existing and future Quartam tools will help you get to the bottom of things: use the server module and Quartam PDF Library to dynamically generate documents for your web applications; and once Quartam Reports hits version 2.0, you'll be able to leverage this report generation tool on the browser platform as well.
Good times...
Subscribe to:
Posts (Atom)