Skip to content

Commit

Permalink
add utility function “splitToTwoLines” for label printer
Browse files Browse the repository at this point in the history
  • Loading branch information
zetavg committed Jan 6, 2024
1 parent b5b81bd commit 7747524
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 16 deletions.
3 changes: 3 additions & 0 deletions App/app/consts/chars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PUNCTUATION_REGEX = /[ ,-/() ]/;
export const LINE_SPLITTING_PUNCTUATION_REGEX = /[,-/(]/;
export const T_PUNCTUATION_REGEX = /[ ,-/ ]/;
59 changes: 59 additions & 0 deletions App/app/features/label-printers/print-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { breakWords, countChars, splitToTwoLines } from './print-utils';

describe('countChars', () => {
it('will return correct char size for CJK characters', async () => {
expect(countChars('你好')).toBe(4);
expect(countChars('你好,world')).toBe(11);
expect(countChars('你好,世界')).toBe(10);
expect(countChars('你好,世界!')).toBe(12);
expect(countChars('你好,世界。')).toBe(12);
});
});

describe('breakWords', () => {
it('will break words', async () => {
expect(breakWords('hello world')).toStrictEqual(['hello', 'world']);
});
});

describe('splitToTwoLines', () => {
it('splits a string into two lines', async () => {
expect(splitToTwoLines('Hello World', 12)).toStrictEqual([
'Hello World',
'',
]);
expect(splitToTwoLines('Hello World', 8)).toStrictEqual(['Hello', 'World']);
});

it('splits a string with punctuation into two lines', async () => {
expect(
splitToTwoLines('SKÅDIS Pegboard, white, 56x56 cm', 15),
).toStrictEqual(['SKÅDIS Pegboard', 'white, 56x56 cm']);

expect(
splitToTwoLines('SKÅDIS Pegboard, white, 56x56 cm', 16),
).toStrictEqual(['SKÅDIS Pegboard', 'white, 56x56 cm']);

expect(splitToTwoLines('SKÅDIS Pegboard, 56x56 cm', 24)).toStrictEqual([
'SKÅDIS Pegboard',
'56x56 cm',
]);

expect(
splitToTwoLines('SKÅDIS Pegboard, 56x56 cm, white', 24),
).toStrictEqual(['SKÅDIS Pegboard', '56x56 cm, white']);

expect(
splitToTwoLines('SKÅDIS Pegboard (56x56 cm, white)', 24),
).toStrictEqual(['SKÅDIS Pegboard', '(56x56 cm, white)']);

// Not supported yet
// expect(
// splitToTwoLines('SKÅDIS Pegboard (56x56 cm, white)', 30),
// ).toStrictEqual(['SKÅDIS Pegboard', '(56x56 cm, white)']);

expect(
splitToTwoLines('SKÅDIS Pegboard - 56x56 cm - white', 24),
).toStrictEqual(['SKÅDIS Pegboard', '56x56 cm - white']);
});
});
50 changes: 47 additions & 3 deletions App/app/features/label-printers/print-utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { Platform } from 'react-native';

import {
LINE_SPLITTING_PUNCTUATION_REGEX,
PUNCTUATION_REGEX,
T_PUNCTUATION_REGEX,
} from '@app/consts/chars';

import LinguisticTaggerModuleIOS from '@app/modules/LinguisticTaggerModuleIOS';

export function breakWords(words: string) {
if (Platform.OS === 'ios') {
return LinguisticTaggerModuleIOS.cut(words);
} else {
return words.split(' ');
return words.split(PUNCTUATION_REGEX).filter(s => !!s);
}
}

Expand All @@ -18,6 +24,9 @@ export function countChars(str: string) {
count +
// https://github.com/vinta/pangu.js/blob/7cd72c9/src/shared/core.js#L3-L12
((code >= 0x2e80 && code <= 0x2eff) || // CJK Radicals Supplement
(code >= 0x3000 && code <= 0x303f) || // CJK Symbols and Punctuation
(code >= 0xff01 && code <= 0xff60) || // Halfwidth and Fullwidth Forms (only Fullwidth, part 1)
(code >= 0xffe0 && code <= 0xffe6) || // Halfwidth and Fullwidth Forms (only Fullwidth, part 2)
(code >= 0x2f00 && code <= 0x2fdf) || // Kangxi Radicals
(code >= 0x3040 && code <= 0x309f) || // Hiragana
(code >= 0x30a0 && code <= 0x30ff) || // Katakana
Expand All @@ -32,8 +41,8 @@ export function countChars(str: string) {
}, 0);
}

function* generateSubstrings(str: string) {
const words = breakWords(str);
function* generateSubstrings(str: string, splitRegex?: RegExp) {
const words = splitRegex ? str.split(splitRegex) : breakWords(str);
let wordsPtr = 0;
let subStr = '';
let lastYieldLength = 0;
Expand Down Expand Up @@ -66,11 +75,46 @@ export function getNextLine(
};
}

export function splitToTwoLines(
str: string,
line1MaxWidth: number,
): [string, string] {
const strContainsPunctuation = LINE_SPLITTING_PUNCTUATION_REGEX.test(str);

let line1 = '';
for (const substring of generateSubstrings(
str,
strContainsPunctuation ? LINE_SPLITTING_PUNCTUATION_REGEX : undefined,
)) {
if (countChars(substring) > line1MaxWidth) break;

line1 = substring;
}

const line2 = str.slice(line1.length);
return [line1, line2].map(s => trimStringByRegex(s, T_PUNCTUATION_REGEX)) as [
string,
string,
];
}

function trimStringByRegex(str: string, regex: RegExp) {
// Create a regex pattern that matches the characters at the start and end of the string
let combinedRegex = new RegExp(
'^' + regex.source + '+|' + regex.source + '+$',
'g',
);

// Replace the matched characters with an empty string
return str.replace(combinedRegex, '');
}

export const utils = {
breakWords,
countChars,
generateSubstrings,
getNextLine,
splitToTwoLines,
};

export default utils;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
import { Alert, Linking, ScrollView, TextInput } from 'react-native';
import type { StackScreenProps } from '@react-navigation/stack';

import Clipboard from '@react-native-clipboard/clipboard';
import { diff } from 'deep-object-diff';

import { URLS } from '@app/consts/info';
Expand Down Expand Up @@ -196,6 +197,40 @@ function NewOrEditLabelPrinterModalScreen({
}
}, [loadSampleConfig, state.printerConfig]);

const pasteConfigFromClipboard = useCallback(async () => {
printerConfigInputRef.current?.blur();
const cfg = await Clipboard.getString();
setState(s => ({
...s,
printerConfig: cfg,
}));
}, []);
const handlePasteConfigPress = useCallback(() => {
if (
state.printerConfig !== INITIAL_PRINTER_CONFIG &&
state.printerConfig !== SAMPLE_PRINTER_CONFIG
) {
Alert.alert(
'Paste and replace?',
'Are you sure you want to paste the config from the clipboard, replacing your current config?',
[
{
text: 'Cancel',
style: 'cancel',
onPress: () => {},
},
{
text: 'Paste and replace',
style: 'destructive',
onPress: pasteConfigFromClipboard,
},
],
);
} else {
pasteConfigFromClipboard();
}
}, [pasteConfigFromClipboard, state.printerConfig]);

return (
<ModalContent
navigation={navigation}
Expand Down Expand Up @@ -238,28 +273,35 @@ function NewOrEditLabelPrinterModalScreen({
ref={printerConfigInputRef}
label="Config"
placeholder={
'Paste config here...\n\n(It is recommended to edit the config in a code editor and copy-paste it here)\n'
'Enter config here...\n\n(It is recommended to edit the config in a code editor and copy-paste it here)\n'
}
value={state.printerConfig}
controlElement={
<>
{!!configErrorMessage && (
{!state.printerConfig && (
<UIGroup.ListTextInputItemButton
onPress={handleLoadSampleConfigPress}
>
Load Sample
</UIGroup.ListTextInputItemButton>
)}
<UIGroup.ListTextInputItemButton
onPress={() =>
navigation.push('TestPrinterConfigModal', {
printerConfig: state.printerConfig,
})
}
disabled={!!configErrorMessage}
onPress={handlePasteConfigPress}
>
{configErrorMessage ? 'Invalid Config' : 'Test'}
Paste from Clipboard
</UIGroup.ListTextInputItemButton>
{!!state.printerConfig && (
<UIGroup.ListTextInputItemButton
onPress={() =>
navigation.push('TestPrinterConfigModal', {
printerConfig: state.printerConfig,
})
}
disabled={!!configErrorMessage}
>
{configErrorMessage ? 'Invalid Config' : 'Test'}
</UIGroup.ListTextInputItemButton>
)}
</>
}
keyboardType="ascii-capable"
Expand All @@ -271,6 +313,7 @@ function NewOrEditLabelPrinterModalScreen({
onChangeText={text => setState({ ...state, printerConfig: text })}
{...kiaTextInputProps}
/>
{/*
<UIGroup.ListItemSeparator />
<UIGroup.ListItem
button
Expand All @@ -282,6 +325,7 @@ function NewOrEditLabelPrinterModalScreen({
}
disabled={!!configErrorMessage}
/>
*/}
</UIGroup>
<UIGroup footer="Check out the documentation on how to integrate with your label printer.">
<UIGroup.ListItem
Expand Down
12 changes: 10 additions & 2 deletions App/app/modules/LinguisticTaggerModuleIOS.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { NativeModules } from 'react-native';

import { PUNCTUATION_REGEX } from '@app/consts/chars';

const { NSLinguisticTaggerModule } = NativeModules;

const LinguisticTaggerModuleIOS = {
initTagger() {
return NSLinguisticTaggerModule.initTagger();
if (NSLinguisticTaggerModule) {
return NSLinguisticTaggerModule.initTagger();
}
},
cut(string: string): ReadonlyArray<string> {
return NSLinguisticTaggerModule.cut(string);
if (NSLinguisticTaggerModule) {
return NSLinguisticTaggerModule.cut(string);
} else {
return string.split(PUNCTUATION_REGEX).filter(s => !!s);
}
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1581,8 +1581,6 @@ describe('saveDatum', () => {
{
__type: 'item',
__id: item.__id,
name: 'Item',
icon_color: 'gray',
},
{ ignoreConflict: true, forceTouch: true },
);
Expand Down

0 comments on commit 7747524

Please sign in to comment.