pbm2utf8

A small tool that creates a textual representation of a PBM file using half and full-height block drawing characters.

#include<stdlib.h>
#include<stdio.h>
#include<wchar.h>
#include<stdint.h>
#include<string.h>
#include<locale.h>
#include<ctype.h>

static const wchar_t full_width_blocks[] = { 0x0020, 0x2580, 0x2584, 0x2588 };

int main(int argc, char **argv)
{
  uint64_t width,
           height,
           width_in_bytes;

  uint8_t *top,
          *bottom,
           white_space;

  setlocale(LC_ALL, "");

  // Read header.
  if (scanf("P4 %d %d%c", &width, &height, &white_space) != 3) {
      fprintf(
            stderr,
            "Unable to read header.  "
            "Is there a comment in the file?  We don't support that, yet.\n"
      );
  }

  if (!isspace(white_space)) {
      fprintf(
            stderr,
            "This almost looks like a netpbm file.  "
            "But the delimeter was not whitespace?\n"
      );
  }

  // Allocate memory
  width_in_bytes = (width >> 3) + (width & 7 ? 1 : 0);
  top = malloc(width_in_bytes << 1);
  bottom = top + width_in_bytes;

  if (top == NULL) {
      fprintf(stderr, "Out of memory.\n");
  }

  while (height) {
      // Read first row.
      if (fread(top, width_in_bytes, 1, stdin) != 1) {
          fprintf(
              stderr,
              "Tried to read %d bytes for an even line but failed.\n",
              width_in_bytes
          );
      }

      if (--height) {
          // Read second row
          if (fread(bottom, width_in_bytes, 1, stdin) != 1) {
              fprintf(
                  stderr,
                  "Tried to read %d bytes for an odd line but failed.\n",
                  width_in_bytes
              );
          }
          --height;
      } else {
          // There are no more rows, blank the bottom row.
          memset(bottom, 0, width_in_bytes);
      }

      // Write the row.
      for (uint64_t bit = 0; bit < width; ++bit) {
          uint8_t top_byte = top[bit >> 3],
              bottom_byte = bottom[bit >> 3],
              mask = 128 >> (bit&7);

          printf(
              "%lc",
              full_width_blocks[
                  (top_byte    & mask ? 1 : 0) |
                  (bottom_byte & mask ? 2 : 0)
              ]
          );
      }

      puts("");
  }

  return 0;
}

This page is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.