Has anyone used jagPDF on c++?
I am trying to learn how to use it from the handbook they provide at http://www.jagpdf.org/doc/index.htm. Unfortunately, all their examples are in Python. Which sometimes is not a big deal to adapt to C++, but sometimes it's confusing.
For example, I am trying to learn how to align text, which in the tutorial is at Text Aligment. Once I find the padding, the function that puts the padding in the line of text is:
canvas.text(txt, [padding / 2.0], [0])
looking in the reference for that function, the translation table is:
[py] text(txt_u, offsets, positions)
[c++] void text(Char const* txt_u, Double const* offsets, UInt offsets_length, Int const* positions, UInt positions_length);
Parameters:
txt_u: zero terminated string.
offsets: glyph offsets expressed in glyph space (i.e. in thousandths of a unit of text space).
offsets_length: number of offsets.
position: associates glyph offsets with glyph indices in txt_u.
positions_length: number of positions.
I have tried several things, but I haven't figure out the two additional input parameters that c++ requires over Python. If someone out there has used jagPDF and knows how to do it in c++, I'd be greatly appreciative.
This is my first touch to JagPDF. There is section Text Alignment, but code is written on Python.
In C++ it should be like:
// line width
pdf::Double line_width = 597.6;
// text to show
pdf::Char *txt = "Everything should be made as simple as possible, but no simpler.";
// calculate text width
pdf::Double text_width = font.advance(txt);
// calculate gap - difference in line_width and text_width
pdf::Double gap = line_width - text_width;
// calculate the text padding
pdf::Double padding = -1000.0 / font.size() * gap;
// create array with paddings - I set all padings, but is used only 1
pdf::Double const pp[] = { padding, padding / 2.0 }; // index 0 for right alignment, 1 for center
// not sure what are positions, but need for function `text()` - set as Doc to zero (0)
pdf::Int const dd[] = { 0};
// get canvas where the text is written
pdf::Canvas canvas = doc.page().canvas();
// start text block
canvas.text_start(0, 480);
// set font
canvas.text_font(font);
// right alignment text
canvas.text(txt, pp, 1, dd, 1);
canvas.text_translate_line(0, font.height());
// center alignment text
canvas.text(txt, &pp[1], 1, dd, 1);
canvas.text_translate_line(0, font.height());
// finish text block
canvas.text_end();
Hope it helps
Related
I have a QGraphicsView which contains many QGraphicsItem like rectangle, polylines etc.
I want to name each rectangle and name is on the rectangle.
Name should be centrally aligned i.e. half of the string name characters should be left side of mid position and half of the characters should be right side of mid position.
For that I used following psuedo code:
int firstPosition = GetMidPosition () - (strLength / 2);
int secondPosition = some points
DrawText( firstPosition , secondPosition );
Now if I print co-ordinates of firstPosition, secondPosition , GetMidPosition() they comes perfectly as I expect.
But, the text does not come properly. I have to manually adjust it.
int firstPosition = GetMidPosition () - 6 *(strLength / 2);
Now it appears centrally. Why this is happening ? Co-ordinates are correct then why I have to manually adjust it.
I want to avoid that adjustment (i.e. 6 * )
If tomorrow, I changed the font size of text, then I will have to
change the multiplication factor 6 into something else. That should
not be happened. How to write general purpose logic for this ?
I'm trying to print an invoice document on A4 in millimetres instead of the default device units. Except that when changing the units to millimetres the pointsize of text on the printed document no longer matches up with the pointsize in for instance Word or Adobe Illustrator. I tried converting the point size to the corresponding pixel size, but they had issues.
QFont::SetPixelSize only takes an int so if the calculations falls below 1 it will trunctuate to 0
font.setPixelSize((9.0 * 72.0) / printer.resolution());
And the other method made the text the correct vertical size, but there are some artefacts:
int phys_w = printer.width();
font.setPointSizeF((9.0 / phys_w) * 210.0);
Where you can see the unusually large gaps between some characters. (Perhaps there is some of precision issue inside Qt its text drawing code?)
Here is a minimal example showing the issues:
QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPrinter::A4);
printer.setOrientation(QPrinter::Portrait);
printer.setFullPage(true);
printer.setPageMargins(QMarginsF(0, 0, 0, 0));
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName("invoice.pdf");
QPainter painter(&printer);
auto page_size = printer.pageRect(QPrinter::Unit::Millimeter);
painter.setWindow(page_size.toRect());
QFont font = painter.font();
// Either this
font.setPixelSize((9.0 * 72.0) / printer.resolution());
// or this
int phys_w = printer.width();
font.setPointSizeF((9.0 / phys_w) * 210.0);
painter.setFont(font);
painter.drawText(35, 46, "John Doe");
How can I have the positioning in Millimetres (or any arbitrary unit) and have the text size be correct in points (or some correct recalculation)?
This is on Qt 5.10.0 and Windows 10.
EDIT
In the end I opted to go for a 10x scale increase (so tenths of a millimetre) which fixed the kerning issues visible for setPointSizeF. Now the last issue I'm having with the scale is that of setting the width of a line and other shapes (QPen::setWidth) and I cant find a calculation so it's in millimetres.
EDIT
In the end the linewidth didn't need any recalculations. The final code is below:
QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPrinter::A4);
printer.setOrientation(QPrinter::Portrait);
printer.setFullPage(true);
printer.setPageMargins(QMarginsF(0, 0, 0, 0));
printer.setOutputFormat(QPrinter::NativeFormat);
QPainter painter(&printer);
painter.setWindow(0, 0, 2100, 2970);
painter.setViewport(0, 0, printer.width(), printer.height());
QFont font(fontFamily, 0, weight, italic);
font.setPointSizeF(static_cast<float>(pixelSize) / printer.width() * 2100);
I think you're dividing where you should multiply and vice versa. Take a look with the units written out explicitly:
9 points * (1 inch / 72 points) * (printer.res pixels/inch)
multiplying the units, the numerator gets (points * inch * pixels) , denominator gets (points * inch) . Cancel out the like units and you get pixels in the numerator. So the calculation should be:
font.setPixelSize(9.0 / 72.0 * printer.resolution());
For your second problem,
QPen::setWidthF(w*printer.resolution()/25.4);
where w is your desired width in mm.
By making detailed observations (printing a long sentence) it's noticed that these gaps between characters are directly related to characters themselves, wide characters and Capital letters eat up the gap (J, s , r, c) while narrow characters leave more gap (i , l , t) .. this is just to say its not a random behavior.
In general this is known as kerning nicely explained here. To minimize this, Qt sets QFont kerning to true (default) QFont Kerning ... but the problem is that kerning is much dependent on the used font and pixel, and Qt kerning enabled sometimes has not effect as in this post, probably because setting Kerning to true
(Will apply kerning between adjacent glyphs. Note that OpenType GPOS
based kerning is currently not supported QRawFont::LayoutFlags
Which means that some font Ascent / Descent will still cause limitation how the gap is controlled.
Two solutions are considered below, the first is with still sticking to default painter font, yet you can stretch the characters to enforce reasonable gab:
font.setStretch(70); // value is experimental
I don't think this is a robust solution, unless one is limited to use specific font, while font itself is not important for invoice printing, the second attractive approach is to find out best font that meets requirements: render well with set resolution / little loss when drawn to PDF (from Qt) / and most important efficient for kerning!
"MS Gothic" (ttf) for example performs well for such setup, here how it performs (without stretch)
painter.setFont(QFont("MS Gothic"));
QFont font = painter.font();
int phys_w = printer.width();
font.setPointSizeF((9.0 / phys_w) * 210.0);
//font.setStretch(70);
painter.setFont(font);
The good thing about selecting a suitable font is that more control is possible (especially for invoice printing where small space is valuable).
For example the same can printed with less space by reducing the gap between letters:
font.setLetterSpacing(QFont::PercentageSpacing,65); // 65% gap of default
And one can go even for lower font size without loosing visual clearance.
sharing below some work while trying to clarify how printing small font size is affecting gap between letters. the idea is to draw each character separately inside QRect and at the same time draw equivalent QRectF on same area.
That's to see when font size is large QRectF are drawn fit next to each other, so also each character ... while when font size goes low the adjacent QRectFs will severely overlap and gaps between characters start to get disordered.
QPen pen=painter.pen();
pen.setWidth(0.1);
painter.setPen(pen);
QString td("John Doe");
auto spacer = font.pointSizeF(); // font size used to set width of QRect of each character.
spacer *=30.0; // large Rect width.
auto b = 35.0;
for (int i=0; i < td.length() ;i++ )
{
QRectF rectf(b+=spacer,47.0,4.0,4.0);
QRect rect(b, 47.0,4.0,4.0);
QString ch = td.at(i);
//painter.drawText(b+=spacer,46,ch);
painter.drawText(rect,Qt::AlignCenter,ch);
painter.drawRect(rectf);
}
painter.end();
Result for large font size:
Next make QRectF overlap:
spacer *=10.0;
Result, letters get less gaps and wide adjacent characters get narrow gap.
Making some experiments with ttf fonts, and tried to use famous font rendering library FreeType, version 2.5.3.
My code:
#include "ft2build.h"
#include FT_FREETYPE_H
#define FONTPATH "<font path here>"
const char* fontfile=FONTPATH "<fontname here>.TTF";
const int w=25;
const int h=25;
char* outbitmap;
int main() {
outbitmap=new char[w*h];
memset(outbitmap,0,w*h);
FT_Library ftl;
FT_Error err=FT_Init_FreeType(&ftl);
FT_Face fface;
err=FT_New_Face(ftl,fontfile,0,&fface);
err=FT_Set_Pixel_Sizes(fface,w,h);
FT_UInt ch;
//ch=0x3042; //あ
//ch='_';
ch='|';
FT_UInt chridx=FT_Get_Char_Index(fface,ch);
err=FT_Load_Glyph(fface,chridx,FT_LOAD_DEFAULT);
err=FT_Render_Glyph(fface->glyph,FT_RENDER_MODE_NORMAL);
for(int y=0;y<fface->glyph->bitmap.rows;++y) {
int outy=fface->glyph->bitmap.rows-fface->glyph->bitmap_top+y; //???how to get baseline position
for(int x=0;x<fface->glyph->bitmap.width;++x) {
int outx=fface->glyph->bitmap_left+x;
outbitmap[outy*w+outx]=fface->glyph->bitmap.buffer[fface->glyph->bitmap.width*y+x];
}
}
delete[] outbitmap;
err=FT_Done_Face(fface);
err=FT_Done_FreeType(ftl);
return 0;
}
So I have some questions.
Assume that I need render one character to byte array with fixed size, in correct position.
Character size must be exactly size that fits in output bitmap, no clipping allowed.
It's OK to ignore kerning completely.
I specified character width=height=25. But for '|' it gives fface->glyph->bitmap.rows==26.
How should I set height to get exactly 25px output, not 26, for any normal font? If it isn't
possible, then is there any way to calculate output character height exactly in pixels before
FT_Render_Glyph. FT_Set_Pixel_Sizes doesn't work well enough, so I getting +1px sometimes.
How do I get baseline position for any given font? If I have baseline, I can place character
in exactly right position. I have no 1000x1000 screen, just one bitmap 25x25.
This is super late but I just found out how to do this:
(Getting the character height in pixels). From what I understand setting the font size (e.g. with FT_Set_Char_Size) really just defines a scaling between font units and pixel units (this explains more). I.e., 12pt font at 72dpi does not necessarily mean the glyph is 12 pixels high. You can, however, get a bounding box that I am pretty sure will enclose every single glyph in the chosen font. This is how I did it:
//12pt font
double font_size = 12.0;
//Resolution means DPI here
double pixel_size = font_size * resolution / 72;
//Font height and width in pixels
int font_height = round((face->bbox.yMax - face->bbox.yMin)*pixel_size / face->units_per_EM);
int font_width = round((face->bbox.xMax - face->bbox.xMin)*pixel_size / face->units_per_EM);
This is how I got a baseline height. It's just the maximum descent below the baseline of any glyph. When paired with the above code, drawing your glyph with the baseline at this position should guarantee that all glyphs will fit in the bounding box.
//Convert this to pixel_size if your DPI is not 72
double font_size = 12.0;
//Height in pixels (using a double for sub-pixel precision)
double baseline_height = abs(face->descender) * font_size / face->units_per_EM;
(Where face is the font you have loaded).
EDIT: forgot to account for DPI, in my application I was just using 72 so it canceled out
Nobody answered anything, so posting some more workarounds than answers I found by myself.
For question 1, I can probably live with that after applying following:
FT_Set_Pixel_Sizes(fface,w-1,h-1);
or maybe even
FT_Set_Pixel_Sizes(fface,w-w%2,h-h%2);
For question 2 I don't know yet ...
I knew this was going to come back and bite me one day. I'm reading an image, doing a resize to 48 pixels tall (by whatever the width is), then grabbing the total image columns and reading each individual pixel to get the color values. All of this information gets written out to a file. The concise version of the code is this:
unsigned char cols, rows;
unsigned char red, green, blue;
short int myCol, myRow;
cols = processedImage.columns();
rows = processedImage.rows();
myFile.write(reinterpret_cast<const char *>(&cols), sizeof(cols));
for (myCol = cols - 1; myCol >= 0; myCol--) {
for (myRow = rows - 1; myRow >= 0; myRow--) {
Magick::ColorRGB rgb(processedImage.pixelColor(myCol, myRow));
red = rgb.red() * 255;
green = rgb.green() * 255;
blue = rgb.blue() * 255;
myFile.write(reinterpret_cast <const char*> (&red), sizeof(red));
myFile.write(reinterpret_cast <const char*> (&green), sizeof(green));
myFile.write(reinterpret_cast <const char*> (&blue), sizeof(blue));
}
}
The problem here is when the file is wider than what char can hold. For example, I'm processing a file that's 494x48 pixels.
When I look at the (binary) file created, the first line which holds the column count says it's '238'. The next line starts the RGB data:
0: 238 // Column count
1: 255 // Red
2: 0 // Green
3: 0 // Blue
4: 255 // Red
5: 0 // Green
6: 0 // Blue
So I'm stuck. How can I store the actual columns value as a single line in the resulting file?
What about using more than one character instead of one character? Presume there are say 4 characters to store the cols, rows etc. since character can store 0-255, so 4 character will store 256x256x256x256 i.e. 32 bits, long enough
Answering my own question. Thanks to everyone who responded and helped figure out what I was doing wrong. The issue here stems from months of making assumptions based on Arduino code. Arduino has a single INT/UINT and I was using that to read in values from the generated files. I assumed that data type was a uint8_t when in reality I discovered it's really a uint16_t. As it was messing up other parts in the code (namely what position to seek to in a file), I had switched to a char data type as that was only taking up 1-byte. But in doing so I nailed myself with the roll-over issue mentioned above. So the solution, now that I know more about how the data types are within Arduino code:
change the image file processing to use uint16_t for both rows and columns
(since I have access to it) change the reading on the Arduino side to also use uint16_t
change the file seek command to move one more byte after the "header" so the data being
read doesn't get mangled.
And ultimately, I've now stopped using Arduino's built-in data types and switched to platform independent data types that are actually what they say they are.
Chalk this up to another learning experience (in my entire process of actually learning c++) ...
I just need to know how to change the tab size in Qt in a QTextEdit. My Google and stackoverflow search returned me null. Thanks in advance.
If you want to create a source code editor using QTextEdit, you should first assign a fixed-width (monospace) font. This ensures that all characters have the same width:
QFont font;
font.setFamily("Courier");
font.setStyleHint(QFont::Monospace);
font.setFixedPitch(true);
font.setPointSize(10);
QTextEdit* editor = new QTextEdit();
editor->setFont(font);
If you want to set a tab width to certain amount of spaces, as it is typically done in text editors, use QFontMetrics to compute the size of one space in pixels:
const int tabStop = 4; // 4 characters
QFontMetrics metrics(font);
editor->setTabStopWidth(tabStop * metrics.width(' '));
The QTextEdit::tabStopWidth property might solve your problem (see here for Documentation...)
While the question about how to set tab stop width has been answered already; computing the correct tab width in pixels is still (or again?) an open question.
Since Qt 5.10, QTextEdit::tabStopWidth is marked as obsolete and QTextEdit::tabStopDistance was introduced. tabStopWidth was integer, tabStopDistance is double.
Why so complicated?
Setting n * QFontMetrics::width(' ') as tab stop width makes troubles because font_metrics.width returns an integer. Even if you have a monospace standard font, the width of a single character is actually not an integer, so QFontMetrics::width returns an inaccurate measure.
If you compare the appearance of the strings ........| and \t\t\t\t| (\t = tab, n=2), you will see that the pipes are not aligned properly. It will become worse the more tabs you insert.
Solution
You could do what #Ferdinand Beyer proposed, but it will slightly change the typeface. I also had to adapt his method to make it work. However, there's a much simpler approach which exploits that you can set tabStopDistance now with double precision:
static constexpr int tab_width_char = 2;
m_text_edit->setFont(QFont("Courier", 12));
const auto font_metrics = m_text_edit->fontMetrics();
static constexpr int big_number = 1000; // arbitrary big number.
const QString test_string(" ");
// compute the size of a char in double-precision
const int single_char_width = font_metrics.width(test_string);
const int many_char_width = font_metrics.width(test_string.repeated(big_number));
const double single_char_width_double = many_char_width / double(big_number);
// set the tab stop with double precision
m_text_edit->setTabStopDistance(tab_width_char * single_char_width_double);
This would be so much simpler if Qt offered a way to get the width of a single character as double.
While #Ferdinand Beyer's solution will work on some systems, generally fonts are not guaranteed to have integer metrics. e.g 12pt DejaVu Sans Mono on my Linux setup has character width of 9.625. The best solution I've found is add some letter spacing to get pixel-perfect alignment.
int tabstop = 4;
QFontMetricsF fm (ui->textEdit->font());
auto stopWidth = tabstop * fm.width(' ');
auto letterSpacing = (ceil(stopWidth) - stopWidth) / tabstop;
auto font = ui->textEdit->font();
font.setLetterSpacing(QFont::AbsoluteSpacing, letterSpacing);
ui->textEdit->setFont(font);
ui->textEdit->setTabStopWidth(ceil(stopWidth));
Computing a product of a size of one space and num spaces is not always precise (tested under macOS, Monaco font), presumably due to some gaps in between the characters in the real string.
A better solution would be to measure the length of the string containing tabStop spaces:
const int tabStop = 4; // 4 characters
QString spaces;
for (int i = 0; i < tabStop; ++i) {
spaces += " ";
}
QFontMetrics metrics(font);
editor->setTabStopWidth(metrics.width(spaces));