chromium/components/qr_code_generator/qr_print.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This helper binary can be compiled to aid in development / debugging of the
// QR generation code. It prints a QR code to the console and thus allows much
// faster iteration. It is not built by default, see the BUILD.gn in this
// directory.

#include <stdio.h>

#include <optional>
#include <utility>

#include "base/containers/span.h"
#include "base/strings/string_number_conversions.h"
#include "components/qr_code_generator/qr_code_generator.h"

// kTerminalBackgroundIsBright controls the output polarity. Many QR scanners
// will cope with inverted bright / dark but, if you have a bright terminal
// background, you may need to change this.
constexpr bool kTerminalBackgroundIsBright = false;

// kPaint is a pair of UTF-8 encoded code points for U+2588 ("FULL BLOCK").
static constexpr char kPaint[] = "\xe2\x96\x88\xe2\x96\x88";
static constexpr char kNoPaint[] = "  ";

static void PrintHorizontalLine(const char* white, int size) {
  for (int x = 0; x < size + 2; x++) {
    fputs(white, stdout);
  }
  fputs("\n", stdout);
}

int main(int argc, char** argv) {
  // Presubmits don't allow fprintf to a variable called |stderr|.
  FILE* const STDERR = stderr;

  if (argc < 2 || argc > 3) {
    fprintf(STDERR, "Usage: %s <input string> [mask number]\n", argv[0]);
    return 1;
  }

  const uint8_t* const input = reinterpret_cast<const uint8_t*>(argv[1]);
  const size_t input_len = strlen(argv[1]);

  std::optional<uint8_t> mask;
  if (argc == 3) {
    unsigned mask_unsigned;
    if (!base::StringToUint(argv[2], &mask_unsigned) || mask_unsigned > 7) {
      fprintf(STDERR, "Mask numbers run from zero to seven.\n");
      return 1;
    }
    mask = static_cast<uint8_t>(mask_unsigned);
  }

  const char* black = kNoPaint;
  const char* white = kPaint;
  if (kTerminalBackgroundIsBright) {
    std::swap(black, white);
  }

  auto code = qr_code_generator::GenerateCode(
      base::span<const uint8_t>(input, input_len), mask);
  if (!code.has_value()) {
    fprintf(STDERR, "Input too long to be encoded.\n");
    return 2;
  }

  const int size = code->qr_size;
  PrintHorizontalLine(white, size);

  int i = 0;
  for (int y = 0; y < size; y++) {
    fputs(white, stdout);
    for (int x = 0; x < size; x++) {
      fputs((code->data[i++] & 1) ? black : white, stdout);
    }
    fputs(white, stdout);
    fputs("\n", stdout);
  }

  PrintHorizontalLine(white, size);

  return 0;
}