2016
02.04

Yesterday, I found myself in need of a TIFF image splitter. Reason: The scanner in my office didn’t allow me to retain the settings after a document scan. So every time I choose to ‘End Scanning’, the output image quality setting is reset to default. Which means I need change the output settings for each of document that I want to scan. The only way was to scan all documents at one go. That lead me to the situation where a TIFF splitter is needed.

A quick Google-search brought me to a simple, free and open-source program called Tiff Splitter. It is exactly the kind of program that I need. It is simple, and does its job well.

Because it does the job well, I become interested to find out how exactly Tiff Splitter works. Since it’s an open-source, I can immediately take a dive and learn.

TiffSplit_2016-01-28_15-16-40

After we click, select an input file, or, drop a file, the event handlers will eventually call processFile method.

private void processFile(string fileName)
{
   // ... SNIP ...
   
   _configs.inputFile = fileName;

   // Configure the extractor
   RetObj retObj = TiffSplitCode.Prepare(_configs, out numOfPages);
   
   // ... SNIP ...
   
   // Let user select pages to extract
   PageSelection ps = new PageSelection(numOfPages);
   ps.ShowDialog();
   _configs.fromPage = ps.PageFrom;
   _configs.toPage = ps.PageTo;
   _configs.doOverwrite = ps.OverwriteFiles;
   
   // ... SNIP ...
   
   // do the work in separate thread
   backgroundWorkerSplit.RunWorkerAsync(_configs);
}

On the worker thread it will do the splitting

private void backgroundWorkerSplit_DoWork(object sender, DoWorkEventArgs e)
{
	ConfObj input = e.Argument as ConfObj;  
	RetObj retObj = TiffSplitCode.Split(input, _updateProgress);
	e.Result = retObj;
}

The actual work was done by TiffSplitCode

public static RetObj Split(ConfObj input, UpdateProgress updateProgress)
{
	int numOfPages = input.toPage - input.fromPage + 1;                               
	// save each image
	for (int i = 0; i < numOfPages; i++)
	{
		_coder.Save(input.fromPage - 1 + i);
		// ... SNIP ...
	}
	// ... SNIP ...
}


Hmm, it looks simple. But who is _coder? How does it able to distinguish PDF, TIFF or JPG? Different format definitely requires different treatment right (or so I thought) ?

The secret is in Prepare and CoderFactory method.

public static RetObj Prepare(ConfObj input, out int numOfPages)
{
	// .. Snipped: Validate input file ..	

	// overwrite output type if the input is PDF
	if (ext.ToUpper() == ".PDF")
		input.outputType = OutputType.PDF;

	// create output coder
	_coder = CoderFactory(input.outputType);

	// open file
	numOfPages = _coder.LoadImage(input.inputFile);

	// prepare
	_coder.Prepare(input);

	return retobj;
}

static private ICoder CoderFactory(OutputType type)
{
	switch (type)
	{
		case OutputType.TIF:
			return new TiffCoder();
		case OutputType.JPG:
			return new JpegCoder();
		case OutputType.PDF:
			return new PDFCoder();
		default:
			throw new Exception("Unknown output format.");
	}
}


So now it's clear that the actual loading and splitting in classes which implement ICoder interface. So for Tiff, the work is done by TiffCoder

public class TiffCoder : ICoder
{
	private Image _image;
	private FrameDimension _dim;
	
	// ... SNIP ...

	public int LoadImage(string fileName)
	{
		_inputImageName = fileName;
		_image = Image.FromFile(fileName);
		Guid guid = _image.FrameDimensionsList[0];
		_dim = new FrameDimension(guid);
		return _image.GetFrameCount(_dim);
	}


	public void Save(int pageNum)
	{
		_image.SelectActiveFrame(_dim, pageNum);
		string outputFileName = null;

		// ... SNIP: Output file name

		if (!_config.doOverwrite)
		{
			outputFileName = HelperMethods.ModifyFileName(outputFileName);
		}

		_image.Save(outputFileName);
	}

	// ... SNIP ...
}


Well that's interesting. The program actually uses .NET's System.Drawing.Image instead of some external library to handle TIFF. As a dig deeper, I found out that System.Drawing is actually a managed interface to Windows' native library, GDI+ (mind blown!)

I'll stop my exploration here, perhaps in the future I'll have the motivation to dig deeper than today. For more reading please check the following references:

  1. System.Drawing.Image source code
  2. brief introduction to GDI
  3. Microsoft GDI+ page

Worth sharing?

  • Facebook
  • Twitter
  • Google
  • Delicious
  • Digg
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS
2016
01.27

Found via HN, rephrased for future reminder:

  1. Do not repeat yourself, always refactor/reuse, avoid copy-paste
  2. Name your variables for what they are for, not its type of data
  3. Name your methods for what they are going to do
  4. Don’t use magic number or string literals, use constants or readonly variables
  5. Strive to make your method testable (unit test, dependency injection, mock object)
  6. Don’t be afraid to ask for help
  7. See a bug? Fix it while you’re there, then move on
  8. Share your knowledge, it will help the point no. 6
  9. Don’t interrupt colleagues who are ‘in the flow’, related to point no. 6
  10. Take criticism with open-mind, defensive stance might turn this into religious-wars

Original source: 10 golden rules for becoming a better programmer

Worth sharing?

  • Facebook
  • Twitter
  • Google
  • Delicious
  • Digg
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS
2015
10.07

Yesterday I was editing the data of Complete Ferry Schedule using JSON Table Editor in Chrome. Immediately I found that the “Output to JSON” button is no longer works. I opened JSON Table Editor in other browsers (Firefox, Microsoft Edge, Opera) but found that the button works fine.

Looking into Chrome’s Developer Tools, I found why the button was not working.

json.table.security.error

Alas, it’s an Uncaught Security Exception. This is the offending line:

if (opener && opener.JSONTableEditor)
{
	// ... Snipped ...
}

This part of the code was supposed to check if the current window is actually a child of previous instance of JSON Table Editor (this happens when you edit a cell which has value of a complex JavaScript object). If it does, it will update the cell value in the parent window. If it doesn’t, it will show the formatted JSON on the bottom of the page.

The obvious work around would be putting this checking inside a try-catch:

var checkedOpener;
try {
   if (opener && opener.JSONTableEditor)
      checkedOpener = opener;
}
catch (ex) {
   //Nothing to do here
}
if (checkedOpener)
{
	// ... Snipped ...
}

Problem solved!

Final Thoughts

The error is unique to Google Chrome. This bug is already reported to Chromium team on Feb 2015. Unfortunately, until today the bug’s is not yet resolved.

Worth sharing?

  • Facebook
  • Twitter
  • Google
  • Delicious
  • Digg
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS