mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-27 21:02:35 +00:00
checkin for entire port source tree
This commit is contained in:
656
port_src/output/zxing/qrcode/encoder/minimal_encoder.rs
Normal file
656
port_src/output/zxing/qrcode/encoder/minimal_encoder.rs
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* Copyright 2021 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// package com::google::zxing::qrcode::encoder;
|
||||
|
||||
/**
|
||||
* Encoder that encodes minimally
|
||||
*
|
||||
* Algorithm:
|
||||
*
|
||||
* The eleventh commandment was "Thou Shalt Compute" or "Thou Shalt Not Compute" - I forget which (Alan Perilis).
|
||||
*
|
||||
* This implementation computes. As an alternative, the QR-Code specification suggests heuristics like this one:
|
||||
*
|
||||
* If initial input data is in the exclusive subset of the Alphanumeric character set AND if there are less than
|
||||
* [6,7,8] characters followed by data from the remainder of the 8-bit byte character set, THEN select the 8-
|
||||
* bit byte mode ELSE select Alphanumeric mode;
|
||||
*
|
||||
* This is probably right for 99.99% of cases but there is at least this one counter example: The string "AAAAAAa"
|
||||
* encodes 2 bits smaller as ALPHANUMERIC(AAAAAA), BYTE(a) than by encoding it as BYTE(AAAAAAa).
|
||||
* Perhaps that is the only counter example but without having proof, it remains unclear.
|
||||
*
|
||||
* ECI switching:
|
||||
*
|
||||
* In multi language content the algorithm selects the most compact representation using ECI modes.
|
||||
* For example the most compact representation of the string "\u0150\u015C" (O-double-acute, S-circumflex) is
|
||||
* ECI(UTF-8), BYTE(\u0150\u015C) while prepending one or more times the same leading character as in
|
||||
* "\u0150\u0150\u015C", the most compact representation uses two ECIs so that the string is encoded as
|
||||
* ECI(ISO-8859-2), BYTE(\u0150\u0150), ECI(ISO-8859-3), BYTE(\u015C).
|
||||
*
|
||||
* @author Alex Geller
|
||||
*/
|
||||
struct MinimalEncoder {
|
||||
|
||||
let string_to_encode: String;
|
||||
|
||||
let is_g_s1: bool;
|
||||
|
||||
let mut encoders: ECIEncoderSet;
|
||||
|
||||
let ec_level: ErrorCorrectionLevel;
|
||||
}
|
||||
|
||||
impl MinimalEncoder {
|
||||
|
||||
enum VersionSize {
|
||||
|
||||
SMALL("version 1-9"), MEDIUM("version 10-26"), LARGE("version 27-40");
|
||||
|
||||
let description: String;
|
||||
|
||||
fn new( description: &String) -> VersionSize {
|
||||
let .description = description;
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
return self.description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MinimalEncoder
|
||||
*
|
||||
* @param stringToEncode The string to encode
|
||||
* @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm
|
||||
* chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority
|
||||
* charset to encode any character in the input that can be encoded by it if the charset is among the
|
||||
* supported charsets.
|
||||
* @param isGS1 {@code true} if a FNC1 is to be prepended; {@code false} otherwise
|
||||
* @param ecLevel The error correction level.
|
||||
* @see ResultList#getVersion
|
||||
*/
|
||||
fn new( string_to_encode: &String, priority_charset: &Charset, is_g_s1: bool, ec_level: &ErrorCorrectionLevel) -> MinimalEncoder {
|
||||
let .stringToEncode = string_to_encode;
|
||||
let .isGS1 = is_g_s1;
|
||||
let .encoders = ECIEncoderSet::new(&string_to_encode, &priority_charset, -1);
|
||||
let .ecLevel = ec_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the string minimally
|
||||
*
|
||||
* @param stringToEncode The string to encode
|
||||
* @param version The preferred {@link Version}. A minimal version is computed (see
|
||||
* {@link ResultList#getVersion method} when the value of the argument is null
|
||||
* @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm
|
||||
* chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority
|
||||
* charset to encode any character in the input that can be encoded by it if the charset is among the
|
||||
* supported charsets.
|
||||
* @param isGS1 {@code true} if a FNC1 is to be prepended; {@code false} otherwise
|
||||
* @param ecLevel The error correction level.
|
||||
* @return An instance of {@code ResultList} representing the minimal solution.
|
||||
* @see ResultList#getBits
|
||||
* @see ResultList#getVersion
|
||||
* @see ResultList#getSize
|
||||
*/
|
||||
fn encode( string_to_encode: &String, version: &Version, priority_charset: &Charset, is_g_s1: bool, ec_level: &ErrorCorrectionLevel) -> /* throws WriterException */Result<ResultList, Rc<Exception>> {
|
||||
return Ok(MinimalEncoder::new(&string_to_encode, &priority_charset, is_g_s1, ec_level).encode(version));
|
||||
}
|
||||
|
||||
fn encode(&self, version: &Version) -> /* throws WriterException */Result<ResultList, Rc<Exception>> {
|
||||
if version == null {
|
||||
// compute minimal encoding trying the three version sizes.
|
||||
let versions: vec![Vec<Version>; 3] = vec![::get_version(VersionSize::SMALL), ::get_version(VersionSize::MEDIUM), ::get_version(VersionSize::LARGE), ]
|
||||
;
|
||||
let results: vec![Vec<ResultList>; 3] = vec![self.encode_specific_version(versions[0]), self.encode_specific_version(versions[1]), self.encode_specific_version(versions[2]), ]
|
||||
;
|
||||
let smallest_size: i32 = Integer::MAX_VALUE;
|
||||
let smallest_result: i32 = -1;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < 3 {
|
||||
{
|
||||
let size: i32 = results[i].get_size();
|
||||
if Encoder::will_fit(size, versions[i], self.ec_level) && size < smallest_size {
|
||||
smallest_size = size;
|
||||
smallest_result = i;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if smallest_result < 0 {
|
||||
throw WriterException::new("Data too big for any version");
|
||||
}
|
||||
return Ok(results[smallest_result]);
|
||||
} else {
|
||||
// compute minimal encoding for a given version
|
||||
let result: ResultList = self.encode_specific_version(version);
|
||||
if !Encoder::will_fit(&result.get_size(), &::get_version(&::get_version_size(&result.get_version())), self.ec_level) {
|
||||
throw WriterException::new(format!("Data too big for version{}", version));
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_version_size( version: &Version) -> VersionSize {
|
||||
return if version.get_version_number() <= 9 { VersionSize::SMALL } else { if version.get_version_number() <= 26 { VersionSize::MEDIUM } else { VersionSize::LARGE } };
|
||||
}
|
||||
|
||||
fn get_version( version_size: &VersionSize) -> Version {
|
||||
match version_size {
|
||||
SMALL =>
|
||||
{
|
||||
return Version::get_version_for_number(9);
|
||||
}
|
||||
MEDIUM =>
|
||||
{
|
||||
return Version::get_version_for_number(26);
|
||||
}
|
||||
LARGE =>
|
||||
{
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
return Version::get_version_for_number(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_numeric( c: char) -> bool {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
fn is_double_byte_kanji( c: char) -> bool {
|
||||
return Encoder::is_only_double_byte_kanji(&String::value_of(c));
|
||||
}
|
||||
|
||||
fn is_alphanumeric( c: char) -> bool {
|
||||
return Encoder::get_alphanumeric_code(c) != -1;
|
||||
}
|
||||
|
||||
fn can_encode(&self, mode: &Mode, c: char) -> bool {
|
||||
match mode {
|
||||
KANJI =>
|
||||
{
|
||||
return ::is_double_byte_kanji(c);
|
||||
}
|
||||
ALPHANUMERIC =>
|
||||
{
|
||||
return ::is_alphanumeric(c);
|
||||
}
|
||||
NUMERIC =>
|
||||
{
|
||||
return ::is_numeric(c);
|
||||
}
|
||||
// any character can be encoded as byte(s). Up to the caller to manage splitting into
|
||||
BYTE =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// multiple bytes when String.getBytes(Charset) return more than one byte.
|
||||
_ =>
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_compacted_ordinal( mode: &Mode) -> i32 {
|
||||
if mode == null {
|
||||
return 0;
|
||||
}
|
||||
match mode {
|
||||
KANJI =>
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ALPHANUMERIC =>
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
NUMERIC =>
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
BYTE =>
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
throw IllegalStateException::new(format!("Illegal mode {}", mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_edge(&self, edges: &Vec<Vec<Vec<Edge>>>, position: i32, edge: &Edge) {
|
||||
let vertex_index: i32 = position + edge.characterLength;
|
||||
let mode_edges: Vec<Edge> = edges[vertex_index][edge.charsetEncoderIndex];
|
||||
let mode_ordinal: i32 = ::get_compacted_ordinal(edge.mode);
|
||||
if mode_edges[mode_ordinal] == null || mode_edges[mode_ordinal].cachedTotalSize > edge.cachedTotalSize {
|
||||
mode_edges[mode_ordinal] = edge;
|
||||
}
|
||||
}
|
||||
|
||||
fn add_edges(&self, version: &Version, edges: &Vec<Vec<Vec<Edge>>>, from: i32, previous: &Edge) {
|
||||
let mut start: i32 = 0;
|
||||
let mut end: i32 = self.encoders.length();
|
||||
let priority_encoder_index: i32 = self.encoders.get_priority_encoder_index();
|
||||
if priority_encoder_index >= 0 && self.encoders.can_encode(&self.string_to_encode.char_at(from), priority_encoder_index) {
|
||||
start = priority_encoder_index;
|
||||
end = priority_encoder_index + 1;
|
||||
}
|
||||
{
|
||||
let mut i: i32 = start;
|
||||
while i < end {
|
||||
{
|
||||
if self.encoders.can_encode(&self.string_to_encode.char_at(from), i) {
|
||||
self.add_edge(edges, from, Edge::new(Mode::BYTE, from, i, 1, previous, version));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if self.can_encode(Mode::KANJI, &self.string_to_encode.char_at(from)) {
|
||||
self.add_edge(edges, from, Edge::new(Mode::KANJI, from, 0, 1, previous, version));
|
||||
}
|
||||
let input_length: i32 = self.string_to_encode.length();
|
||||
if self.can_encode(Mode::ALPHANUMERIC, &self.string_to_encode.char_at(from)) {
|
||||
self.add_edge(edges, from, Edge::new(Mode::ALPHANUMERIC, from, 0, if from + 1 >= input_length || !self.can_encode(Mode::ALPHANUMERIC, &self.string_to_encode.char_at(from + 1)) { 1 } else { 2 }, previous, version));
|
||||
}
|
||||
if self.can_encode(Mode::NUMERIC, &self.string_to_encode.char_at(from)) {
|
||||
self.add_edge(edges, from, Edge::new(Mode::NUMERIC, from, 0, if from + 1 >= input_length || !self.can_encode(Mode::NUMERIC, &self.string_to_encode.char_at(from + 1)) { 1 } else { if from + 2 >= input_length || !self.can_encode(Mode::NUMERIC, &self.string_to_encode.char_at(from + 2)) { 2 } else { 3 } }, previous, version));
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_specific_version(&self, version: &Version) -> /* throws WriterException */Result<ResultList, Rc<Exception>> {
|
||||
let input_length: i32 = self.string_to_encode.length();
|
||||
// Array that represents vertices. There is a vertex for every character, encoding and mode. The vertex contains
|
||||
// a list of all edges that lead to it that have the same encoding and mode.
|
||||
// The lists are created lazily
|
||||
// The last dimension in the array below encodes the 4 modes KANJI, ALPHANUMERIC, NUMERIC and BYTE via the
|
||||
// function getCompactedOrdinal(Mode)
|
||||
let edges: [[[Option<Edge>; 4]; self.encoders.length()]; input_length + 1] = [[[None; 4]; self.encoders.length()]; input_length + 1];
|
||||
self.add_edges(version, edges, 0, null);
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i <= input_length {
|
||||
{
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < self.encoders.length() {
|
||||
{
|
||||
{
|
||||
let mut k: i32 = 0;
|
||||
while k < 4 {
|
||||
{
|
||||
if edges[i][j][k] != null && i < input_length {
|
||||
self.add_edges(version, edges, i, edges[i][j][k]);
|
||||
}
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let minimal_j: i32 = -1;
|
||||
let minimal_k: i32 = -1;
|
||||
let minimal_size: i32 = Integer::MAX_VALUE;
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < self.encoders.length() {
|
||||
{
|
||||
{
|
||||
let mut k: i32 = 0;
|
||||
while k < 4 {
|
||||
{
|
||||
if edges[input_length][j][k] != null {
|
||||
let edge: Edge = edges[input_length][j][k];
|
||||
if edge.cachedTotalSize < minimal_size {
|
||||
minimal_size = edge.cachedTotalSize;
|
||||
minimal_j = j;
|
||||
minimal_k = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if minimal_j < 0 {
|
||||
throw WriterException::new(format!("Internal error: failed to encode \"{}\"", self.string_to_encode));
|
||||
}
|
||||
return Ok(ResultList::new(version, edges[input_length][minimal_j][minimal_k]));
|
||||
}
|
||||
|
||||
struct Edge {
|
||||
|
||||
let mode: Mode;
|
||||
|
||||
let from_position: i32;
|
||||
|
||||
let charset_encoder_index: i32;
|
||||
|
||||
let character_length: i32;
|
||||
|
||||
let previous: Edge;
|
||||
|
||||
let cached_total_size: i32;
|
||||
}
|
||||
|
||||
impl Edge {
|
||||
|
||||
fn new( mode: &Mode, from_position: i32, charset_encoder_index: i32, character_length: i32, previous: &Edge, version: &Version) -> Edge {
|
||||
let .mode = mode;
|
||||
let .fromPosition = from_position;
|
||||
let .charsetEncoderIndex = if mode == Mode::BYTE || previous == null { charset_encoder_index } else { // inherit the encoding if not of type BYTE
|
||||
previous.charsetEncoderIndex };
|
||||
let .characterLength = character_length;
|
||||
let .previous = previous;
|
||||
let mut size: i32 = if previous != null { previous.cachedTotalSize } else { 0 };
|
||||
let need_e_c_i: bool = mode == Mode::BYTE && // at the beginning and charset is not ISO-8859-1
|
||||
(previous == null && let .charsetEncoderIndex != 0) || (previous != null && let .charsetEncoderIndex != previous.charsetEncoderIndex);
|
||||
if previous == null || mode != previous.mode || need_e_c_i {
|
||||
size += 4 + mode.get_character_count_bits(version);
|
||||
}
|
||||
match mode {
|
||||
KANJI =>
|
||||
{
|
||||
size += 13;
|
||||
break;
|
||||
}
|
||||
ALPHANUMERIC =>
|
||||
{
|
||||
size += if character_length == 1 { 6 } else { 11 };
|
||||
break;
|
||||
}
|
||||
NUMERIC =>
|
||||
{
|
||||
size += if character_length == 1 { 4 } else { if character_length == 2 { 7 } else { 10 } };
|
||||
break;
|
||||
}
|
||||
BYTE =>
|
||||
{
|
||||
size += 8 * encoders.encode(&string_to_encode.substring(from_position, from_position + character_length), charset_encoder_index).len();
|
||||
if need_e_c_i {
|
||||
// the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long
|
||||
size += 4 + 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
cached_total_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ResultList {
|
||||
|
||||
let list: List<ResultList.ResultNode> = ArrayList<>::new();
|
||||
|
||||
let version: Version;
|
||||
}
|
||||
|
||||
impl ResultList {
|
||||
|
||||
fn new( version: &Version, solution: &Edge) -> ResultList {
|
||||
let mut length: i32 = 0;
|
||||
let mut current: Edge = solution;
|
||||
let contains_e_c_i: bool = false;
|
||||
while current != null {
|
||||
length += current.characterLength;
|
||||
let previous: Edge = current.previous;
|
||||
let need_e_c_i: bool = current.mode == Mode::BYTE && // at the beginning and charset is not ISO-8859-1
|
||||
(previous == null && current.charsetEncoderIndex != 0) || (previous != null && current.charsetEncoderIndex != previous.charsetEncoderIndex);
|
||||
if need_e_c_i {
|
||||
contains_e_c_i = true;
|
||||
}
|
||||
if previous == null || previous.mode != current.mode || need_e_c_i {
|
||||
list.add(0, ResultNode::new(current.mode, current.fromPosition, current.charsetEncoderIndex, length));
|
||||
length = 0;
|
||||
}
|
||||
if need_e_c_i {
|
||||
list.add(0, ResultNode::new(Mode::ECI, current.fromPosition, current.charsetEncoderIndex, 0));
|
||||
}
|
||||
current = previous;
|
||||
}
|
||||
// If there is no ECI at the beginning then we put an ECI to the default charset (ISO-8859-1)
|
||||
if is_g_s1 {
|
||||
let mut first: ResultNode = list.get(0);
|
||||
if first != null && first.mode != Mode::ECI && contains_e_c_i {
|
||||
// prepend a default character set ECI
|
||||
list.add(0, ResultNode::new(Mode::ECI, 0, 0, 0));
|
||||
}
|
||||
first = list.get(0);
|
||||
// prepend or insert a FNC1_FIRST_POSITION after the ECI (if any)
|
||||
list.add( if first.mode != Mode::ECI { 0 } else { 1 }, ResultNode::new(Mode::FNC1_FIRST_POSITION, 0, 0, 0));
|
||||
}
|
||||
// set version to smallest version into which the bits fit.
|
||||
let version_number: i32 = version.get_version_number();
|
||||
let lower_limit: i32;
|
||||
let upper_limit: i32;
|
||||
match ::get_version_size(version) {
|
||||
SMALL =>
|
||||
{
|
||||
lower_limit = 1;
|
||||
upper_limit = 9;
|
||||
break;
|
||||
}
|
||||
MEDIUM =>
|
||||
{
|
||||
lower_limit = 10;
|
||||
upper_limit = 26;
|
||||
break;
|
||||
}
|
||||
LARGE =>
|
||||
{
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
lower_limit = 27;
|
||||
upper_limit = 40;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let size: i32 = self.get_size(version);
|
||||
// increase version if needed
|
||||
while version_number < upper_limit && !Encoder::will_fit(size, &Version::get_version_for_number(version_number), ec_level) {
|
||||
version_number += 1;
|
||||
}
|
||||
// shrink version if possible
|
||||
while version_number > lower_limit && Encoder::will_fit(size, &Version::get_version_for_number(version_number - 1), ec_level) {
|
||||
version_number -= 1;
|
||||
}
|
||||
let .version = Version::get_version_for_number(version_number);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the size in bits
|
||||
*/
|
||||
fn get_size(&self) -> i32 {
|
||||
return self.get_size(self.version);
|
||||
}
|
||||
|
||||
fn get_size(&self, version: &Version) -> i32 {
|
||||
let mut result: i32 = 0;
|
||||
for let result_node: ResultNode in self.list {
|
||||
result += result_node.get_size(version);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* appends the bits
|
||||
*/
|
||||
fn get_bits(&self, bits: &BitArray) -> /* throws WriterException */Result<Void, Rc<Exception>> {
|
||||
for let result_node: ResultNode in self.list {
|
||||
result_node.get_bits(bits);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_version(&self) -> Version {
|
||||
return self.version;
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
let result: StringBuilder = StringBuilder::new();
|
||||
let mut previous: ResultNode = null;
|
||||
for let current: ResultNode in self.list {
|
||||
if previous != null {
|
||||
result.append(",");
|
||||
}
|
||||
result.append(¤t.to_string());
|
||||
previous = current;
|
||||
}
|
||||
return result.to_string();
|
||||
}
|
||||
|
||||
struct ResultNode {
|
||||
|
||||
let mode: Mode;
|
||||
|
||||
let from_position: i32;
|
||||
|
||||
let charset_encoder_index: i32;
|
||||
|
||||
let character_length: i32;
|
||||
}
|
||||
|
||||
impl ResultNode {
|
||||
|
||||
fn new( mode: &Mode, from_position: i32, charset_encoder_index: i32, character_length: i32) -> ResultNode {
|
||||
let .mode = mode;
|
||||
let .fromPosition = from_position;
|
||||
let .charsetEncoderIndex = charset_encoder_index;
|
||||
let .characterLength = character_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the size in bits
|
||||
*/
|
||||
fn get_size(&self, version: &Version) -> i32 {
|
||||
let mut size: i32 = 4 + self.mode.get_character_count_bits(version);
|
||||
match self.mode {
|
||||
KANJI =>
|
||||
{
|
||||
size += 13 * self.character_length;
|
||||
break;
|
||||
}
|
||||
ALPHANUMERIC =>
|
||||
{
|
||||
size += (self.character_length / 2) * 11;
|
||||
size += if (self.character_length % 2) == 1 { 6 } else { 0 };
|
||||
break;
|
||||
}
|
||||
NUMERIC =>
|
||||
{
|
||||
size += (self.character_length / 3) * 10;
|
||||
let rest: i32 = self.character_length % 3;
|
||||
size += if rest == 1 { 4 } else { if rest == 2 { 7 } else { 0 } };
|
||||
break;
|
||||
}
|
||||
BYTE =>
|
||||
{
|
||||
size += 8 * self.get_character_count_indicator();
|
||||
break;
|
||||
}
|
||||
ECI =>
|
||||
{
|
||||
// the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long
|
||||
size += 8;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the length in characters according to the specification (differs from getCharacterLength() in BYTE mode
|
||||
* for multi byte encoded characters)
|
||||
*/
|
||||
fn get_character_count_indicator(&self) -> i32 {
|
||||
return if self.mode == Mode::BYTE { self.encoders.encode(&self.string_to_encode.substring(self.from_position, self.from_position + self.character_length), self.charset_encoder_index).len() } else { self.character_length };
|
||||
}
|
||||
|
||||
/**
|
||||
* appends the bits
|
||||
*/
|
||||
fn get_bits(&self, bits: &BitArray) -> /* throws WriterException */Result<Void, Rc<Exception>> {
|
||||
bits.append_bits(&self.mode.get_bits(), 4);
|
||||
if self.character_length > 0 {
|
||||
let length: i32 = self.get_character_count_indicator();
|
||||
bits.append_bits(length, &self.mode.get_character_count_bits(self.version));
|
||||
}
|
||||
if self.mode == Mode::ECI {
|
||||
bits.append_bits(&self.encoders.get_e_c_i_value(self.charset_encoder_index), 8);
|
||||
} else if self.character_length > 0 {
|
||||
// append data
|
||||
Encoder::append_bytes(&self.string_to_encode.substring(self.from_position, self.from_position + self.character_length), self.mode, bits, &self.encoders.get_charset(self.charset_encoder_index));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
let result: StringBuilder = StringBuilder::new();
|
||||
result.append(self.mode).append('(');
|
||||
if self.mode == Mode::ECI {
|
||||
result.append(&self.encoders.get_charset(self.charset_encoder_index).display_name());
|
||||
} else {
|
||||
result.append(&self.make_printable(&self.string_to_encode.substring(self.from_position, self.from_position + self.character_length)));
|
||||
}
|
||||
result.append(')');
|
||||
return result.to_string();
|
||||
}
|
||||
|
||||
fn make_printable(&self, s: &String) -> String {
|
||||
let result: StringBuilder = StringBuilder::new();
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < s.length() {
|
||||
{
|
||||
if s.char_at(i) < 32 || s.char_at(i) > 126 {
|
||||
result.append('.');
|
||||
} else {
|
||||
result.append(&s.char_at(i));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user