Ես կարող եմ հաջողությամբ ստեղծել QR Code PNG պատկերներ ZXing-ի միջոցով, բայց հեշտ ճանապարհ չկա ելքը SVG կամ EPS ստանալու համար:
Ինչպե՞ս կարող եմ վեկտորային պատկեր ստեղծել BitMatrix օբյեկտից, որը ստեղծվել է QRCodeWriter-ի կողմից:
Ես կարող եմ հաջողությամբ ստեղծել QR Code PNG պատկերներ ZXing-ի միջոցով, բայց հեշտ ճանապարհ չկա ելքը SVG կամ EPS ստանալու համար:
Ինչպե՞ս կարող եմ վեկտորային պատկեր ստեղծել BitMatrix օբյեկտից, որը ստեղծվել է QRCodeWriter-ի կողմից:
Հին հարց ես գիտեմ, բայց բոլորի համար, ովքեր գալիս են, ովքեր փնտրում են, թե ինչպես դա անել... շատ հեշտ է միացնել ZXing
-ին JFreeSVG
-ին (http://www.jfree.org/jfreesvg), օրինակ.
package org.jfree.demo;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import org.jfree.graphics2d.svg.SVGGraphics2D;
import org.jfree.graphics2d.svg.SVGUtils;
public class QRCodes {
public static void main(String[] args) throws WriterException, IOException {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode("http://www.jfree.org/jfreesvg",
BarcodeFormat.QR_CODE, 160, 160);
int w = bitMatrix.getWidth();
SVGGraphics2D g2 = new SVGGraphics2D(w, w);
g2.setColor(Color.BLACK);
for (int xIndex = 0; xIndex < w; xIndex = xIndex + bitMatrix.getRowSize()) {
for (int yIndex = 0; yIndex < w; yIndex = yIndex + bitMatrix.getRowSize()) {
if (bitMatrix.get(xIndex, yIndex)) {
g2.fillRect(xIndex, yIndex, bitMatrix.getRowSize(), bitMatrix.getRowSize());
}
}
}
SVGUtils.writeToSVG(new File("qrtest.svg"), g2.getSVGElement());
}
}
Դուք նույնիսկ կարող եք դա անել միայն zxing-ով առանց լրացուցիչ գրադարանների.
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
BitMatrix bitMatrix = qrCodeWriter.encode(payload, BarcodeFormat.QR_CODE, 543, 543, hints);
StringBuilder sbPath = new StringBuilder();
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int rowSize = bitMatrix.getRowSize();
BitArray row = new BitArray(width);
for(int y = 0; y < height; ++y) {
row = bitMatrix.getRow(y, row);
for(int x = 0; x < width; ++x) {
if (row.get(x)) {
sbPath.append(" M"+x+","+y+"h1v1h-1z");
}
}
}
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ").append(width).append(" ").append(height).append("\" stroke=\"none\">\n");
sb.append("<style type=\"text/css\">\n");
sb.append(".black {fill:#000000;}\n");
sb.append("</style>\n");
sb.append("<path class=\"black\" d=\"").append(sbPath.toString()).append("\"/>\n");
sb.append("</svg>\n");
Խնդրում ենք նկատի ունենալ, որ վերը նշված լուծումը շատ ավելի արդյունավետ է հիշողության սպառման առումով, քան Batik's DOM-ն օգտագործող լուծումը:
style
տարրն այստեղ ավելորդ է (լրացումը ուղու համար լռելյայն սև է. տե՛ս developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill#path): Անհասկանալի է, թե ինչու են այստեղ օգտագործվում երկու լարային կառուցողներ. ավելի պարզ է պարզապես ավելացնել XML-ի առաջին մասը մեկ շինարարի վրա, այնուհետև շրջել զանգվածի միջով՝ ավելացնելով պիքսելներ, ապա ավելացնել մնացած XML-ը: M
-ից առաջ տարածությունը լիովին ընտրովի է և միայն օգնում է վերջնական արդյունքը ընթեռնելի դարձնել: 17.03.2021 Ամենահեշտ ձևը, որը ես գտա, iText-ով PDF ստեղծելն էր, այնուհետև ստացված PDF-ը EPS-ի կամ SVG-ի վերածելն էր: Ահա PDF-ը ստեղծելու կոդը.
@Test
public void testQRtoPDF() throws WriterException, FileNotFoundException, DocumentException, UnsupportedEncodingException {
final int s = 600;
int r = 1;
Charset charset = Charset.forName( "UTF-8" );
CharsetEncoder encoder = charset.newEncoder();
byte[] b = null;
try {
// Convert a string to UTF-8 bytes in a ByteBuffer
ByteBuffer bbuf = encoder.encode( CharBuffer.wrap(
"1éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò1" +
"2éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò2" +
"3éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò3" +
"4éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò4" +
"5éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò5" +
"6éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò6" ) );
b = bbuf.array();
} catch ( CharacterCodingException e ) {
System.out.println( e.getMessage() );
}
String content = new String( b, "UTF-8" );
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>( 2 );
hints.put( EncodeHintType.CHARACTER_SET, "UTF-8" );
BitMatrix qrCode = qrCodeWriter.encode( content, BarcodeFormat.QR_CODE, s, s, hints );
Document doc = new Document( new Rectangle( s, s ) );
PdfWriter pdfWriter = PdfWriter.getInstance( doc, new FileOutputStream( "qr-code.pdf" ) );
doc.open();
PdfContentByte contentByte = pdfWriter.getDirectContent();
contentByte.setColorFill( BaseColor.BLACK );
boolean d = false;
for ( int x = 0; x < qrCode.getWidth(); x += r ) {
for ( int y = 0; y < qrCode.getHeight(); y += r ) {
if ( qrCode.get( x, y ) ) {
contentByte.rectangle( x, s - y, r, r );
contentByte.fill();
contentByte.stroke();
}
}
}
doc.close();
}
Այնուհետև ես օգտագործում եմ պատկերի մոգություն փոխակերպման համար: Այսպես.
convert qr-code.pdf qr-code.eps
նույնը ՉԻ կարող անել svg-ի համար
convert qr-code.pdf qr-code.svg
սա չի աշխատում
Ես փորձարկեցի այս կոդը որոշ երկար բովանդակությամբ և այն աշխատում էր մինչև 600 նիշով: Սա, հավանաբար, պայմանավորված է հեռախոսի կամ էկրանի տեսախցիկի ճշգրտությամբ:
Հուսով եմ, որ սա օգնում է ինչ-որ մեկին
Դուք կարող եք օգտագործել Apache Batik
-ը JFreeSVG
-ի փոխարեն, եթե ցանկանում եք ավելի ազատ լիցենզիա: Դա հատկապես իմաստալից է, երբ դուք օգտագործում եք Apache FOP
-ը, որը ներառում է Apache Batik
-ը որպես անցողիկ կախվածություն:
Ահա ճշգրտված տարբերակը. Գաղափարի և օրիգինալ ծածկագրի վարկերը պատկանում են Դեյվիդ Գիլբերտին:
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.svg.SVGDocument;
import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
public class QRCodesBatik {
public static void main(String[] args) throws WriterException, IOException {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode("https://xmlgraphics.apache.org/batik/",
BarcodeFormat.QR_CODE, 800, 800);
// Create a document with the appropriate namespace
DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
SVGDocument document = (SVGDocument) domImpl.createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null);
// Create an instance of the SVG Generator
org.apache.batik.svggen.SVGGraphics2D g2 = new org.apache.batik.svggen.SVGGraphics2D(document);
// draw onto the SVG Graphics object
g2.setColor(Color.BLACK);
for (int xIndex = 0; xIndex < bitMatrix.getWidth(); xIndex = xIndex + bitMatrix.getRowSize()) {
for (int yIndex = 0; yIndex < bitMatrix.getWidth(); yIndex = yIndex + bitMatrix.getRowSize()) {
if (bitMatrix.get(xIndex, yIndex)) {
g2.fillRect(xIndex, yIndex, bitMatrix.getRowSize(), bitMatrix.getRowSize());
}
}
}
try (Writer out = new OutputStreamWriter(new FileOutputStream(new File("qrtest.svg")), "UTF-8")) {
g2.stream(out, true);
}
}
}
Svg ձևաչափը նախատեսված է ուղղանկյունների մի շարք, որը կառուցում է BitMatrix-ը: Ձևաչափի գաղափարը պատկերի լուծումն է, որը չպետք է ազդի չափի որևէ աճի վրա: Օրինակ, @siom-ի լուծումը ստեղծում է 1x1 ուղղանկյուններ բոլոր պիքսելների համար (այդ իսկ պատճառով այն ստեղծում է հսկայական ֆայլ): Ես այդ լուծումը դասում եմ որպես բիրտ ուժի մոտեցում այս խնդրին։
Ես ավելի լավ լուծում եմ մշակել, որն աշխատում է O(n^2-ում): Այն սկանավորում է ամբողջ bitMatrix-ը և սկզբում հայտնաբերում է առավելագույն երկարության քառակուսին յուրաքանչյուր կետում, այնուհետև այն փորձում է ուղղանկյունի հասցնել քառակուսին յուրաքանչյուր հարթության մեջ: Վերջապես այն գծում է բոլոր հնարավոր ուղղանկյունները և քառակուսիները տարբեր չափերի:
import com.google.zxing.common.BitMatrix;
import java.awt.Color;
import java.awt.Point;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import org.jfree.graphics2d.svg.SVGGraphics2D;
public class SvgUtils {
public static byte[] createSvgImage(BitMatrix bitMatrix){
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
Set<Point> visitedPoints = new HashSet<>();
SVGGraphics2D g2 = new SVGGraphics2D(width, height);
g2.setColor(Color.BLACK);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Point maxRectangleLength = getMaxRectangleLength(new Point(x, y), bitMatrix, visitedPoints);
if(maxRectangleLength != null) {
g2.fillRect(x, y, maxRectangleLength.x, maxRectangleLength.y);
y += maxRectangleLength.y-1;
}
}
}
return g2.getSVGDocument().getBytes(StandardCharsets.UTF_8);
}
private static int getMaxSquareLength(Point startPoint, BitMatrix bitMatrix, Set<Point> visitedPoints){
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int maxLength = 0;
while(startPoint.x + maxLength < width && startPoint.y + maxLength < height) {
for (int xOffSett = 0; xOffSett < maxLength; xOffSett++) {
if (!bitMatrix.get(startPoint.x + xOffSett, startPoint.y + maxLength) || visitedPoints.contains(new Point(startPoint.x + xOffSett, startPoint.y + maxLength))) {
return maxLength;
}
}
for (int yOffset = 0; yOffset <= maxLength; yOffset++) {
if (!bitMatrix.get(startPoint.x + maxLength, startPoint.y + yOffset) || visitedPoints.contains(new Point(startPoint.x + maxLength, startPoint.y + yOffset))) {
return maxLength;
}
}
for (int xOffSett = 0; xOffSett < maxLength; xOffSett++) {
visitedPoints.add(new Point(startPoint.x + xOffSett, startPoint.y + maxLength));
}
for (int yOffset = 0; yOffset <= maxLength; yOffset++) {
visitedPoints.add(new Point(startPoint.x + maxLength, startPoint.y + yOffset));
}
maxLength++;
}
return maxLength;
}
private static Point getMaxRectangleLength(Point startPoint, BitMatrix bitMatrix, Set<Point> visitedPoints){
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int maxSquareLength = getMaxSquareLength(startPoint, bitMatrix, visitedPoints);
if(maxSquareLength == 0)
return null;
int maxWidth = maxSquareLength-1;
int maxHeight = maxSquareLength-1;
boolean searchFinished = false;
while(!searchFinished && startPoint.y + ++maxHeight < height) {
for (int xOffSett = 0; xOffSett < maxSquareLength; xOffSett++) {
if (!bitMatrix.get(startPoint.x + xOffSett, startPoint.y + maxHeight) ||
visitedPoints.contains(new Point(startPoint.x + xOffSett, startPoint.y + maxHeight))) {
searchFinished = true;
break;
}
}
}
searchFinished = false;
while(!searchFinished && startPoint.x + ++maxWidth < width) {
for (int yOffSett = 0; yOffSett < maxSquareLength; yOffSett++) {
if (!bitMatrix.get(startPoint.x + maxWidth, startPoint.y + yOffSett) ||
visitedPoints.contains(new Point(startPoint.x + maxWidth, startPoint.y + yOffSett))) {
searchFinished = true;
break;
}
}
}
if(maxHeight >= maxWidth){
for(int yOffSet = maxSquareLength; yOffSet < maxHeight; yOffSet++){
for (int xOffSett = 0; xOffSett < maxSquareLength; xOffSett++) {
visitedPoints.add(new Point(startPoint.x + xOffSett, startPoint.y + yOffSet));
}
}
return new Point(maxSquareLength, maxHeight);
} else {
for(int xOffSett = maxSquareLength; xOffSett < maxWidth; xOffSett++){
for (int yOffSet = 0; yOffSet < maxSquareLength; yOffSet++) {
visitedPoints.add(new Point(startPoint.x + xOffSett, startPoint.y + yOffSet));
}
}
return new Point(maxWidth, maxSquareLength);
}
}
}