Support multi-column output.
This commit is contained in:
parent
3b36b76ef6
commit
5eb93bce96
|
@ -1,4 +1,4 @@
|
|||
# Copyright© 2021 John Sennesael
|
||||
# Copyright© 2022 John Sennesael
|
||||
#
|
||||
# This file is part of justify.
|
||||
#
|
||||
|
@ -29,6 +29,7 @@ set(CMAKE_CXX_STANDARD 20)
|
|||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
add_executable(justify
|
||||
"src/columns.cpp"
|
||||
"src/justify.cpp"
|
||||
"src/justifyCenter.cpp"
|
||||
"src/justifyCommon.cpp"
|
||||
|
|
11
README.md
11
README.md
|
@ -46,3 +46,14 @@ The standard cmake routine:
|
|||
Justify method (default=fill).
|
||||
-h,--help Show this help text.
|
||||
|
||||
## Screenshots
|
||||
|
||||
![2 column output example](./justify_2_column.png)
|
||||
|
||||
![3 column output example](./justify_3_column.png)
|
||||
|
||||
![2 column output with 5 character spacing example](./justify_2_column_5_char_spacing.png)
|
||||
|
||||
![2 column output with max. height of 20 characters per block example](./justify_2_column_max_20_tall.png)
|
||||
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
|
@ -0,0 +1,184 @@
|
|||
#include "columns.h"
|
||||
|
||||
#include "justifyCommon.h"
|
||||
#include "stringHelper.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
Possible layout and what the variables mean:
|
||||
|
||||
columnWidth columnHorizontalSpacing
|
||||
/ \ /\
|
||||
/ \/ \
|
||||
BLA BLA BLAAA ASDFASDFASDFA \
|
||||
BLA BLA BLA.. ASDF ASDF ASD \ row = a vector of columns that all start
|
||||
BLABLABLABLA. ASDASDASDASDA / on the same line, but may end on
|
||||
BLABLA FILLERTEXTLAL / a different line.
|
||||
\ / ASDASD /
|
||||
\ / \
|
||||
block \ columnVerticalSpacing
|
||||
/
|
||||
MORE TEXT !!! FOO FOO BAR.. \
|
||||
ASDF ASDF ASD Fin. \ another row
|
||||
ASD FOO. /
|
||||
|
||||
An individual row can not contain more LINES than maxColumnHeight.
|
||||
|
||||
*/
|
||||
|
||||
// A block is a set of lines.
|
||||
using block = std::vector<std::wstring>;
|
||||
// ... but we also deal with sets of blocks. (eg, for representing a row)
|
||||
using blocks = std::vector<block>;
|
||||
|
||||
std::vector<std::wstring> columns(
|
||||
const std::vector<std::wstring>& in,
|
||||
size_t numCols,
|
||||
size_t columnWidth,
|
||||
size_t columnHorizontalSpacing,
|
||||
size_t columnVerticalSpacing,
|
||||
size_t maxColumnHeight,
|
||||
size_t width)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
|
||||
// Basic sanity check.
|
||||
if ((columnWidth == 0) || (maxColumnHeight == 0)) return result;
|
||||
|
||||
// Puncutation/characters that we can potentially consider a good spot
|
||||
// to start a new block on if a line ends in it.
|
||||
static std::array<wchar_t, 10> const blockEnders = {
|
||||
'!', '?', '.', ';', '}', ']', '>'
|
||||
};
|
||||
|
||||
// The input text (in variable) should already be justified and have a
|
||||
// max text width per line of columnWidth.
|
||||
|
||||
// We are going to iterate though it, and break it up in blocks.
|
||||
// We dont't always break on exactly maxColumnHeight, instead we try
|
||||
// to find a natural spot NEAR the maxColumnHeight, such as at the end
|
||||
// of a paragraph or at the end of a sentence.
|
||||
|
||||
block currentBlock;
|
||||
blocks allBlocks;
|
||||
for(auto lineIt = in.begin(); lineIt != in.end(); ++lineIt)
|
||||
{
|
||||
std::wstring line = *lineIt;
|
||||
std::wstring nextLine;
|
||||
auto itNext = lineIt + 1;
|
||||
if (itNext != in.end())
|
||||
{
|
||||
nextLine = *itNext;
|
||||
}
|
||||
// The block needs to be exactly columnWidth wide to ensure alignment
|
||||
// is correct when we add the hspacing + next column. If the line is
|
||||
// shorter, we have to pad with spaces.
|
||||
while (line.length() < columnWidth)
|
||||
{
|
||||
line += L" ";
|
||||
}
|
||||
currentBlock.emplace_back(line);
|
||||
|
||||
// How far away from the bottom (=max height)?
|
||||
const int distanceToMaxHeight = maxColumnHeight - currentBlock.size();
|
||||
|
||||
// If we have more than maxColumnHeight lines, it's time to start a new
|
||||
// block no matter what.
|
||||
if (distanceToMaxHeight <= 0)
|
||||
{
|
||||
allBlocks.emplace_back(currentBlock);
|
||||
currentBlock.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, are we past 2/3 down?
|
||||
if (distanceToMaxHeight < (maxColumnHeight / 3))
|
||||
{
|
||||
// If the current or next line starts a new paragraph, let's just
|
||||
// start a new block here.
|
||||
if (line.empty() || nextLine.empty())
|
||||
{
|
||||
allBlocks.emplace_back(currentBlock);
|
||||
currentBlock.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Still here? ok... are we 1/4th of the way down?
|
||||
if (distanceToMaxHeight < (maxColumnHeight / 4))
|
||||
{
|
||||
// We're almost to the end, we should probably start looking for a
|
||||
// good spot to break now if we haven't found a new paragraph yet.
|
||||
// Any line ending with punctuation would be good.
|
||||
|
||||
const wchar_t lastChar = line[line.size() - 1];
|
||||
if (std::any_of(blockEnders.begin(), blockEnders.end(),
|
||||
[&lastChar](wchar_t c){
|
||||
return c == lastChar;
|
||||
}))
|
||||
{
|
||||
allBlocks.emplace_back(currentBlock);
|
||||
currentBlock.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're still here, we'll just keep iterating until we hit the
|
||||
// maximum column height.
|
||||
}
|
||||
|
||||
// Cool! we have blocks now - now we have to arrange them into ROWS.
|
||||
// Each row will have N columns where
|
||||
// N = width - columnHorizontalSpacing / columnWidth
|
||||
blocks::iterator curBlockIt = allBlocks.begin();
|
||||
while (curBlockIt != allBlocks.end())
|
||||
{
|
||||
// Make a vector with all the blocks that will make up the current row.
|
||||
blocks blocksThisRow;
|
||||
for (size_t i = 0 ; i != numCols; ++i)
|
||||
{
|
||||
blocksThisRow.emplace_back(*curBlockIt);
|
||||
++curBlockIt;
|
||||
if (curBlockIt == allBlocks.end()) break;
|
||||
}
|
||||
// Figure out how tall this row is going to be (how many lines), by
|
||||
// looking for the biggest block int the vector we just made.
|
||||
size_t curRowHeight{0};
|
||||
for (auto& aBlock: blocksThisRow)
|
||||
{
|
||||
if (aBlock.size() > curRowHeight) curRowHeight = aBlock.size();
|
||||
}
|
||||
// We also add the vertical spacing here to append blank rows in the
|
||||
// loop below.
|
||||
curRowHeight += columnVerticalSpacing;
|
||||
// For every line (0 to curRowHeight), grab a piece out of each block.
|
||||
std::wstring outLine;
|
||||
for (size_t nLine = 0; nLine != curRowHeight; ++nLine)
|
||||
{
|
||||
for (auto& iBlock: blocksThisRow)
|
||||
{
|
||||
if (iBlock.size() < nLine + 1)
|
||||
{
|
||||
// If this block has no content for this line,
|
||||
// fill with spaces instead.
|
||||
outLine.append(columnWidth + columnHorizontalSpacing, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
outLine += iBlock[nLine];
|
||||
outLine.append(columnHorizontalSpacing, ' ');
|
||||
}
|
||||
}
|
||||
// We'll have leftover spacing on the right, trim it off.
|
||||
outLine = StringRightTrim(outLine);
|
||||
result.emplace_back(outLine);
|
||||
outLine.clear();
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Justify is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Justify. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::wstring> columns(
|
||||
const std::vector<std::wstring>& in,
|
||||
size_t numCols,
|
||||
size_t columnWidth,
|
||||
size_t columnHorizontalSpacing,
|
||||
size_t columnVerticalSpacing,
|
||||
size_t maxColumnHeight,
|
||||
size_t width);
|
188
src/justify.cpp
188
src/justify.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "justify.h"
|
||||
|
||||
#include "columns.h"
|
||||
#include "justifyCenter.h"
|
||||
#include "justifyFill.h"
|
||||
#include "justifyLeft.h"
|
||||
|
@ -36,23 +37,46 @@
|
|||
|
||||
void usage(const std::string& program)
|
||||
{
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "justify - simple text alignment tool." << std::endl;
|
||||
std::cout << "Copyright© 2021 John Sennesael" << std::endl << std::endl;
|
||||
std::cout << "Usage: " << program << " [OPTION]..." << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << " -i,--input [FILE] Input file to process, defaults to "
|
||||
<< "stdin." << std::endl;
|
||||
std::cout << " -o,--output [FILE] Output file, defaults to stdout."
|
||||
<< std::endl;
|
||||
std::cout << " -c,--cols [NUM] Text width to format text into."
|
||||
<< std::endl
|
||||
<< " (defaults to terminal width)"
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::endl << "Input / output options:"
|
||||
<< std::endl << std::endl;
|
||||
std::cout << " -i,--input [FILE] Input file to process, defaults to "
|
||||
<< "stdin." << std::endl;
|
||||
std::cout << " -o,--output [FILE] Output file, defaults to stdout."
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::endl << "Column options:" << std::endl << std::endl;
|
||||
std::cout << " -c,--cols [COLS] Number of columns for multi-column"
|
||||
<< std::endl
|
||||
<< " output." << std::endl;
|
||||
std::cout << " -h,--hspacing [COLS] Column horizontal spacing."
|
||||
<< std::endl
|
||||
<< " (default: 2)" << std::endl;
|
||||
std::cout << " -v,--vspacing [ROWS] Column vertical spacing."
|
||||
<< std::endl
|
||||
<< " (default: 1)" << std::endl;
|
||||
std::cout << " -m,--colheight [ROWS] Column max. height."
|
||||
<< std::endl
|
||||
<< " (default: 0, =infinite)"
|
||||
<< std::endl;
|
||||
std::cout << std::endl << "Alignment options: "
|
||||
<< std::endl << std::endl;
|
||||
std::cout << " -w,--width [NUM] Text width to format text into."
|
||||
<< std::endl
|
||||
<< " (defaults to terminal width)"
|
||||
<< std::endl;
|
||||
std::cout << " -a,--align [center|fill|left|right]" << std::endl
|
||||
<< " Justify method (default=fill)."
|
||||
<< std::endl;
|
||||
std::cout << " -h,--help Show this help text."
|
||||
<< std::endl;
|
||||
<< " Justify method (default=fill)."
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::endl << "Misc options: " << std::endl << std::endl;
|
||||
std::cout << " -h,--help Show this help text."
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
bool checkParam(
|
||||
|
@ -107,6 +131,54 @@ Settings parseArgs(const StrVector& args)
|
|||
}
|
||||
continue;
|
||||
}
|
||||
else if ((arg == "-h") || (arg == "--hspacing"))
|
||||
{
|
||||
if (!checkParam("number", arg, args, it)) return result;
|
||||
try
|
||||
{
|
||||
result.hspacing = std::stol(*it);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Could not parse " << arg << " as a number: ";
|
||||
std::cerr << e.what() << std::endl;
|
||||
usage(args[0]);
|
||||
return result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if ((arg == "-v") || (arg == "--vspacing"))
|
||||
{
|
||||
if (!checkParam("number", arg, args, it)) return result;
|
||||
try
|
||||
{
|
||||
result.vspacing = std::stol(*it);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Could not parse " << arg << " as a number: ";
|
||||
std::cerr << e.what() << std::endl;
|
||||
usage(args[0]);
|
||||
return result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if ((arg == "-m") || (arg == "--colheight"))
|
||||
{
|
||||
if (!checkParam("number", arg, args, it)) return result;
|
||||
try
|
||||
{
|
||||
result.maxColHeight = std::stol(*it);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Could not parse " << arg << " as a number: ";
|
||||
std::cerr << e.what() << std::endl;
|
||||
usage(args[0]);
|
||||
return result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if ((arg == "-a") || (arg == "--align"))
|
||||
{
|
||||
if (!checkParam("justification type", arg, args, it))
|
||||
|
@ -143,6 +215,22 @@ Settings parseArgs(const StrVector& args)
|
|||
result.shouldExit = true;
|
||||
return result;
|
||||
}
|
||||
else if ((arg == "-w") || (arg == "--width"))
|
||||
{
|
||||
if (!checkParam("number", arg, args, it)) return result;
|
||||
try
|
||||
{
|
||||
result.width = std::stol(*it);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Could not parse " << arg << " as a number: ";
|
||||
std::cerr << e.what() << std::endl;
|
||||
usage(args[0]);
|
||||
return result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Unknown option: " << arg << std::endl;
|
||||
|
@ -215,20 +303,69 @@ bool justify(const Settings& settings)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
size_t justifyWidth = settings.width;
|
||||
if (settings.cols > 1)
|
||||
{
|
||||
if (settings.hspacing >= settings.width)
|
||||
{
|
||||
std::cerr << "Error: Horizontal column spacing too big for given "
|
||||
<< "width." << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Figure out the width of a column if we have columns.
|
||||
justifyWidth = settings.width / settings.cols;
|
||||
justifyWidth -= (settings.cols - 1) * settings.hspacing;
|
||||
}
|
||||
std::vector<std::wstring> justifiedText;
|
||||
switch (settings.justify)
|
||||
{
|
||||
case Justification::FILL:
|
||||
return justifyFill(inputStream, outputStream, settings.cols);
|
||||
justifiedText = justifyFill(inputStream, justifyWidth);
|
||||
break;
|
||||
case Justification::CENTER:
|
||||
return justifyCenter(inputStream, outputStream, settings.cols);
|
||||
justifiedText = justifyCenter(inputStream, justifyWidth);
|
||||
break;
|
||||
case Justification::RIGHT:
|
||||
return justifyRight(inputStream, outputStream, settings.cols);
|
||||
justifiedText = justifyRight(inputStream, justifyWidth);
|
||||
break;
|
||||
case Justification::LEFT:
|
||||
return justifyLeft(inputStream, outputStream, settings.cols);
|
||||
justifiedText = justifyLeft(inputStream, justifyWidth);
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Not implemented." << std::endl;
|
||||
return false;
|
||||
};
|
||||
std::vector<std::wstring> output;
|
||||
if (settings.cols > 1)
|
||||
{
|
||||
// unless overridden by the user, the column height will be the number
|
||||
// of justified lines divided by the number of desired columns.
|
||||
size_t maxColHeight = settings.maxColHeight;
|
||||
if (maxColHeight == 0)
|
||||
{
|
||||
maxColHeight = justifiedText.size() / settings.cols;
|
||||
}
|
||||
// Split justified text up into columns.
|
||||
output = columns(
|
||||
justifiedText,
|
||||
settings.cols,
|
||||
justifyWidth,
|
||||
settings.hspacing,
|
||||
settings.vspacing,
|
||||
maxColHeight,
|
||||
settings.width
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
output = justifiedText;
|
||||
}
|
||||
// Print the result to the output stream.
|
||||
for (auto& line: output)
|
||||
{
|
||||
*outputStream << line << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
@ -237,14 +374,23 @@ int main(int argc, char* argv[])
|
|||
Settings settings = parseArgs(arguments);
|
||||
if (settings.error) return 1;
|
||||
if (settings.shouldExit) return 0;
|
||||
if (settings.cols == 0)
|
||||
if (settings.width == 0)
|
||||
{
|
||||
// Get terminal width (to use as a default)
|
||||
/// @TODO make sure this works on most unixes
|
||||
winsize termSize;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &termSize);
|
||||
settings.cols = termSize.ws_col;
|
||||
settings.width = termSize.ws_col;
|
||||
}
|
||||
return justify(settings) == true ? 0 : 1;
|
||||
int retval{0};
|
||||
try {
|
||||
retval = justify(settings) == true ? 0 : 1;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
retval = 2;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -29,8 +29,12 @@ struct Settings
|
|||
{
|
||||
size_t cols{};
|
||||
bool error{false};
|
||||
bool shouldExit{false};
|
||||
size_t hspacing{2};
|
||||
std::string inFile;
|
||||
std::string outFile;
|
||||
Justification justify{Justification::FILL};
|
||||
size_t maxColHeight{};
|
||||
std::string outFile;
|
||||
bool shouldExit{false};
|
||||
size_t vspacing{1};
|
||||
size_t width{};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -25,25 +25,24 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool justifyCenter(
|
||||
std::vector<std::wstring> justifyCenter(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
std::vector<std::wstring> parsedLines = truncateLinesToFitWidth(
|
||||
in,
|
||||
out,
|
||||
width
|
||||
);
|
||||
for (auto& line: parsedLines)
|
||||
{
|
||||
float numberOfSpacesNeeded = (width - line.size()) / 2.0f;
|
||||
int numberOfSpacesNeeded = (width - line.size()) / 2.0f;
|
||||
while (numberOfSpacesNeeded > 0)
|
||||
{
|
||||
line = L" " + line;
|
||||
--numberOfSpacesNeeded;
|
||||
}
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -20,9 +20,10 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool justifyCenter(
|
||||
std::vector<std::wstring> justifyCenter(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -22,12 +22,12 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::wstring> truncateLinesToFitWidth(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width)
|
||||
{
|
||||
|
||||
|
@ -40,7 +40,10 @@ std::vector<std::wstring> truncateLinesToFitWidth(
|
|||
{
|
||||
if (!bufferDone)
|
||||
{
|
||||
if (!std::getline(*in, line)) bufferDone = true;
|
||||
if (!std::getline(*in, line))
|
||||
{
|
||||
bufferDone = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -50,23 +53,37 @@ std::vector<std::wstring> truncateLinesToFitWidth(
|
|||
if (overflow.empty() && bufferDone) break;
|
||||
line = StringTrim(line);
|
||||
line = StringRemoveDuplicateWhitespace(line);
|
||||
if (line.empty() && !bufferDone)
|
||||
{
|
||||
parsedLines.emplace_back(L"");
|
||||
if (overflow.empty()) continue;
|
||||
}
|
||||
if (!overflow.empty())
|
||||
{
|
||||
line = overflow + L' ' + line;
|
||||
}
|
||||
overflow.clear();
|
||||
if (line.empty())
|
||||
{
|
||||
parsedLines.emplace_back(L"");
|
||||
continue;
|
||||
}
|
||||
auto lastWordPos = line.find_last_of(' ', width);
|
||||
if ((lastWordPos != std::wstring::npos)
|
||||
&& (line.size() > width))
|
||||
if (line.size() > width)
|
||||
{
|
||||
std::wstring outLine = line.substr(0, lastWordPos);
|
||||
overflow = line.substr(lastWordPos, line.length());
|
||||
parsedLines.emplace_back(outLine);
|
||||
if (lastWordPos != std::wstring::npos)
|
||||
{
|
||||
std::wstring outLine = line.substr(0, lastWordPos);
|
||||
overflow = line.substr(lastWordPos, line.length());
|
||||
parsedLines.emplace_back(outLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have a long word that we couldn't fit.
|
||||
// there's really no good options here... either we
|
||||
// truncate it, things still look pretty, but we lost content.
|
||||
// Or we mess up all formatting behind this.
|
||||
// So instead, let's just throw an error.
|
||||
throw std::runtime_error(
|
||||
"Encountered a word that's longer than the supplied width."
|
||||
" Please provide a longer width so it fits."
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -25,5 +25,4 @@
|
|||
|
||||
std::vector<std::wstring> truncateLinesToFitWidth(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -162,15 +162,14 @@ std::vector<std::wstring> injectSpace(
|
|||
return result;
|
||||
}
|
||||
|
||||
bool justifyFill(
|
||||
std::vector<std::wstring> justifyFill(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
|
||||
std::vector<std::wstring> parsedLines = truncateLinesToFitWidth(
|
||||
in,
|
||||
out,
|
||||
width
|
||||
);
|
||||
|
||||
|
@ -187,14 +186,14 @@ bool justifyFill(
|
|||
const size_t spacesNeeded = width - line.length();
|
||||
if (spacesNeeded == 0)
|
||||
{
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
continue;
|
||||
}
|
||||
auto vWords = words(line);
|
||||
const size_t nWords = vWords.size();
|
||||
if (nWords < 1)
|
||||
{
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
continue;
|
||||
}
|
||||
const size_t spacesPerWord = (spacesNeeded + nWords - 1) / nWords;
|
||||
|
@ -202,14 +201,14 @@ bool justifyFill(
|
|||
// avoid injecting an unreasonable amount of whitespace.
|
||||
if (spacesPerWord > nWords)
|
||||
{
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// we need at least 2 words for this to work.
|
||||
if (nWords < 2)
|
||||
{
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -217,7 +216,7 @@ bool justifyFill(
|
|||
// long, don't justify.
|
||||
if (nextLine.empty() && (line.size() <= width))
|
||||
{
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -239,7 +238,7 @@ bool justifyFill(
|
|||
break;
|
||||
}
|
||||
}
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -30,7 +30,6 @@ struct WordScore
|
|||
float score{};
|
||||
};
|
||||
|
||||
bool justifyFill(
|
||||
std::vector<std::wstring> justifyFill(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -25,21 +25,20 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool justifyLeft(
|
||||
std::vector<std::wstring> justifyLeft(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
const std::vector<std::wstring> parsedLines = truncateLinesToFitWidth(
|
||||
in,
|
||||
out,
|
||||
width
|
||||
);
|
||||
for (auto& line: parsedLines)
|
||||
{
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -23,7 +23,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool justifyLeft(
|
||||
std::vector<std::wstring> justifyLeft(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -25,26 +25,25 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool justifyRight(
|
||||
std::vector<std::wstring> justifyRight(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
std::vector<std::wstring> parsedLines = truncateLinesToFitWidth(
|
||||
in,
|
||||
out,
|
||||
width
|
||||
);
|
||||
for (auto& line: parsedLines)
|
||||
{
|
||||
size_t numberOfSpacesNeeded = width - line.size();
|
||||
int numberOfSpacesNeeded = width - line.size();
|
||||
while (numberOfSpacesNeeded > 0)
|
||||
{
|
||||
line = L" " + line;
|
||||
--numberOfSpacesNeeded;
|
||||
}
|
||||
*out << line << std::endl;
|
||||
result.emplace_back(line);
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
@ -23,7 +23,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool justifyRight(
|
||||
std::vector<std::wstring> justifyRight(
|
||||
std::shared_ptr<std::wistream> in,
|
||||
std::shared_ptr<std::wostream> out,
|
||||
size_t width);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright© 2021 John Sennesael
|
||||
Copyright© 2022 John Sennesael
|
||||
|
||||
This file is part of justify.
|
||||
Justify is free software: you can redistribute it and/or modify it under the
|
||||
|
|
Loading…
Reference in New Issue