lindell / JsBarcode

Barcode generation library written in JavaScript that works in both the browser and on Node.js
http://lindell.me/JsBarcode
MIT License
5.5k stars 1.11k forks source link

JsBarcode does not gets scanned after converting it with the help of EVO pdf but works in html, my requirement is for PDF #327

Open BKM17061996 opened 4 years ago

BKM17061996 commented 4 years ago

wodin commented 4 years ago

What does the generated barcode look like after EVO has converted it to a PDF? Does it help if you specify the format? If JsBarcode produces the correct barcode in HTML then it seems this might be a bug with EVO rather than with JsBarcode.

amandhaasil commented 3 years ago

I had the same problem with clj-htmltopdf as well. It works on the html but not on PDF. Is there anyway to set the svg by hand? Maybe a solution with Cavas?

wodin commented 3 years ago

Could you elaborate on what you're trying to accomplish (as opposed to how you're trying to do it)? Also what does an example barcode look like in the HTML vs. after conversion to PDF?

I've been experimenting with React PDF and have got something half working, but not yet usable:

import React from "react";
import {
  Document,
  Font,
  G,
  Page,
  Rect,
  StyleSheet,
  Svg,
  Text,
  View
} from "@react-pdf/renderer";
import ReactPDF from "@react-pdf/renderer";

import JSBarcode from "jsbarcode";

/* Based on JsBarcode's svg.js etc. */

const merge = (old, replaceObj) => ({ ...old, ...replaceObj });

function getEncodingHeight(encoding, options) {
  return options.height + options.marginTop + options.marginBottom;
}

function getBarcodePadding(textWidth, barcodeWidth, options) {
  if (options.displayValue && barcodeWidth < textWidth) {
    if (options.textAlign == "center") {
      return Math.floor((textWidth - barcodeWidth) / 2);
    } else if (options.textAlign == "left") {
      return 0;
    } else if (options.textAlign == "right") {
      return Math.floor(textWidth - barcodeWidth);
    }
  }
  return 0;
}

function calculateEncodingAttributes(encodings, barcodeOptions, context) {
  for (let i = 0; i < encodings.length; i++) {
    var encoding = encodings[i];
    var options = merge(barcodeOptions, encoding.options);

    var textWidth = 20;

    var barcodeWidth = encoding.data.length * options.width;
    encoding.width = Math.ceil(Math.max(textWidth, barcodeWidth));

    encoding.height = getEncodingHeight(encoding, options);

    encoding.barcodePadding = getBarcodePadding(
      textWidth,
      barcodeWidth,
      options
    );
  }
}

function getTotalWidthOfEncodings(encodings) {
  var totalWidth = 0;
  for (let i = 0; i < encodings.length; i++) {
    totalWidth += encodings[i].width;
  }
  return totalWidth;
}

function getMaximumHeightOfEncodings(encodings) {
  var maxHeight = 0;
  for (let i = 0; i < encodings.length; i++) {
    if (encodings[i].height > maxHeight) {
      maxHeight = encodings[i].height;
    }
  }
  return maxHeight;
}

function Background(props) {
  const { width, height, color } = props;

  return (
    <Rect x={0} y={0} width={width} height={height} style={{ fill: color }} />
  );
}

function BarcodeChunk(props) {
  const { binary, padding, options } = props;

  // Creates the barcode out of the encoded binary
  let yFrom;
  if (options.textPosition == "top") {
    yFrom = options.fontSize + options.textMargin;
  } else {
    yFrom = 0;
  }

  let barWidth = 0;
  let x = 0;
  let bars = [];
  for (var b = 0; b < binary.length; b++) {
    x = b * options.width + padding;

    if (binary[b] === "1") {
      barWidth++;
    } else if (barWidth > 0) {
      bars.push({
        x: x - options.width * barWidth,
        y: yFrom,
        width: options.width * barWidth,
        height: options.height
      });
      barWidth = 0;
    }
  }

  // Last draw is needed since the barcode ends with 1
  if (barWidth > 0) {
    bars.push({
      x: x - options.width * (barWidth - 1),
      y: yFrom,
      width: options.width * barWidth,
      height: options.height
    });
  }

  return bars.map((bar, i) => {
    return (
      <Rect key={i} x={bar.x} y={bar.y} width={bar.width} height={bar.height} />
    );
  });
}

function BarcodeText(props) {
  const { text, width, padding, options } = props;

  // Draw the text if displayValue is set
  if (options.displayValue) {
    var x, y, textAnchor;

    if (options.textPosition == "top") {
      y = options.fontSize - options.textMargin;
    } else {
      y = options.height + options.textMargin + options.fontSize;
    }

    // Draw the text in the correct X depending on the textAlign option
    if (options.textAlign == "left" || padding > 0) {
      x = 0;
      textAnchor = "start";
    } else if (options.textAlign == "right") {
      x = width - 1;
      textAnchor = "end";
    }
    // In all other cases, center the text
    else {
      x = width / 2;
      textAnchor = "middle";
    }

    return (
      <Text x={x} y={y} textAnchor={textAnchor} fill={options.lineColor}>
        {text}
      </Text>
    );
  } else {
    return null;
  }
}

function Barcode({ value, options }) {
  let barcode = {};

  JSBarcode(barcode, value, options);
  let encodings = barcode.encodings;

  const defaults = {
    width: 2,
    height: 100,
    // format: "auto",
    displayValue: true,
    // fontOptions: "bold",
    // font: "monospace",
    text: "",
    textAlign: "center",
    textPosition: "bottom",
    textMargin: 2,
    // fontSize: 20,
    background: "#ffffff",
    lineColor: "#000000",
    // margin: 10,
    marginTop: 10,
    marginBottom: 10,
    marginLeft: 10,
    marginRight: 10
    // valid: function(){}
  };
  const mergedOptions = merge(defaults, options);

  calculateEncodingAttributes(encodings, mergedOptions);
  const totalWidth = getTotalWidthOfEncodings(encodings);
  const maxHeight = getMaximumHeightOfEncodings(encodings);
  const width =
    totalWidth + mergedOptions.marginLeft + mergedOptions.marginRight;

  let xs = [mergedOptions.marginLeft];
  encodings.forEach((e) => xs.push(xs[xs.length - 1] + e.width));

  return (
    <Svg
      x={0}
      y={0}
      width={width}
      height={maxHeight}
      viewBox={`0 0 ${width} ${maxHeight}`}
      originX={0}
      originY={0}
    >
      {options.background && (
        <Background
          width={width}
          height={maxHeight}
          color={options.background}
        />
      )}
      {encodings.map((encoding, i) => {
        const encodingOptions = merge(options, encoding.options);

        return (
          <G
            key={i}
            x={xs[i]}
            y={encodingOptions.marginTop}
            fill={encodingOptions.lineColor}
          >
            <BarcodeChunk
              binary={encoding.data}
              padding={encoding.barcodePadding}
              options={encodingOptions}
            />
            <BarcodeText
              text={encoding.text}
              width={encoding.width}
              padding={encoding.barcodePadding}
              options={encodingOptions}
            />
          </G>
        );
      })}
    </Svg>
  );
}

// Create styles
const styles = StyleSheet.create({
  page: {
    flexDirection: "row",
    backgroundColor: "#E4E4E4"
  },
  section: {
    margin: 10,
    padding: 10,
    flexGrow: 1
  }
});

// Create Document Component
const MyDocument = () => (
  <Document>
    <Page size="A4" style={styles.page}>
      <View style={styles.section}>
        <Text>Section #1</Text>
        <Barcode
          value="123456789999"
          options={{ format: "UPC", background: "lightblue" }}
        />
        <Barcode
          value="9501101530003"
          options={{ format: "EAN13", background: "yellow" }}
        />
        <Barcode
          value="7895641245"
          options={{ format: "CODE128", background: "pink" }}
        />
      </View>
    </Page>
  </Document>
);

ReactPDF.render(<MyDocument />, `${__dirname}/../example.pdf`);
image

The CODE128 barcode scans fine, although the formatting is a bit off. The UPC and EAN13 barcodes are obviously completely broken.