WIP: preparations - introducing TextInfo

This commit is contained in:
Matthias Koefferlein 2023-05-07 19:51:15 +02:00
parent 7b4ff5d823
commit e8048d6686
11 changed files with 413 additions and 140 deletions

View File

@ -101,17 +101,22 @@ hershey_count_edges (const std::string &s, unsigned int f)
HersheyFont *fp = fonts [f];
size_t n = 0;
for (const char *cp = s.c_str (); *cp; ++cp) {
for (const char *cp = s.c_str (); *cp; ) {
unsigned char c = (unsigned char) *cp;
if (c == '\012' || c == '\015') {
if (c == '\015' && cp[1] == '\012') {
++cp;
if (tl::skip_newline (cp)) {
// skip new line - they don't contribute edges
} else {
uint32_t c = tl::utf32_from_utf8 (cp);
if (c < fp->end_char && c >= fp->start_char) {
n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start;
} else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start;
}
} else if (c < fp->end_char && c >= fp->start_char) {
n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start;
} else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start;
}
}
@ -142,22 +147,27 @@ hershey_text_box (const std::string &s, unsigned int f)
int w = 0;
int h = fp->ymax;
for (const char *cp = s.c_str (); *cp; ++cp) {
for (const char *cp = s.c_str (); *cp; ) {
if (tl::skip_newline (cp)) {
unsigned char c = (unsigned char) *cp;
if (c == '\012' || c == '\015') {
if (c == '\015' && cp[1] == '\012') {
++cp;
}
if (w > wl) {
wl = w;
}
hl += line_spacing + h - fp->ymin;
w = 0;
} else if (c < fp->end_char && c >= fp->start_char) {
w += fp->chars [c - fp->start_char].width;
} else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
w += fp->chars [invalid_char - fp->start_char].width;
} else {
uint32_t c = tl::utf32_from_utf8 (cp);
if (c < fp->end_char && c >= fp->start_char) {
w += fp->chars [c - fp->start_char].width;
} else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
w += fp->chars [invalid_char - fp->start_char].width;
}
}
}
@ -170,7 +180,7 @@ hershey_text_box (const std::string &s, unsigned int f)
return db::DBox (0, 0, wl, hl);
}
void
void
hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector<db::DPoint> &linestarts)
{
HersheyFont *fp = fonts [f];
@ -179,20 +189,24 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig
int w = 0;
int h = fp->ymax;
for (const char *cp = s.c_str (); *cp; ++cp) {
for (const char *cp = s.c_str (); *cp; ) {
if (tl::skip_newline (cp)) {
unsigned char c = (unsigned char) *cp;
if (c == '\012' || c == '\015') {
if (c == '\015' && cp[1] == '\012') {
++cp;
}
linestarts.push_back (db::DPoint (w, -hl));
hl += line_spacing + h - fp->ymin;
w = 0;
} else if (c < fp->end_char && c >= fp->start_char) {
w += fp->chars [c - fp->start_char].width;
} else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
w += fp->chars [invalid_char - fp->start_char].width;
} else {
uint32_t c = tl::utf32_from_utf8 (cp);
if (c < fp->end_char && c >= fp->start_char) {
w += fp->chars [c - fp->start_char].width;
} else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) {
w += fp->chars [invalid_char - fp->start_char].width;
}
}
}
@ -221,20 +235,18 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig
}
*l = p;
}
}
// ----------------------------------------------------------------------------
// basic_hershey_edge_iterator implementation
basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, unsigned int f, const std::vector<db::DPoint> &line_starts)
: m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_index (0), m_linestarts (line_starts)
: m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_linestarts (line_starts)
{
m_fp = fonts [f];
m_end = (unsigned int) m_string.size ();
m_new_char = true;
mp_cp = m_string.c_str ();
if (m_linestarts.size () == 0) {
if (m_linestarts.empty ()) {
m_linestarts.push_back (db::DPoint (0.0, 0.0));
}
m_pos = m_linestarts [0];
@ -243,66 +255,47 @@ basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s,
bool
basic_hershey_edge_iterator::at_end () const
{
return m_index >= m_end;
return *mp_cp == 0 && m_edge == m_edge_end;
}
db::DEdge
basic_hershey_edge_iterator::get ()
{
while (m_new_char && !at_end ()) {
unsigned char c = (unsigned char) (m_index < m_end ? m_string [m_index] : ' ');
while (m_edge == m_edge_end && *mp_cp) {
m_pos += m_delta;
if (c < m_fp->end_char && c >= m_fp->start_char) {
m_edge = m_edge_end = 0;
m_delta = db::DVector ();
m_edge = m_fp->chars [c - m_fp->start_char].edge_start;
m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end;
m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0);
if (tl::skip_newline (mp_cp)) {
} else if (c != '\012' && c != '\015'
&& invalid_char < m_fp->end_char
&& invalid_char >= m_fp->start_char) {
++m_line;
m_edge = m_fp->chars [invalid_char - m_fp->start_char].edge_start;
m_edge_end = m_fp->chars [invalid_char - m_fp->start_char].edge_end;
m_delta = db::DVector (m_fp->chars [invalid_char - m_fp->start_char].width, 0);
if (m_line >= m_linestarts.size ()) {
db::DPoint last;
last = m_linestarts.back ();
last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing));
m_linestarts.push_back (last);
}
m_pos = m_linestarts [m_line];
} else {
m_edge = m_edge_end = 0;
m_delta = db::DVector ();
uint32_t c = tl::utf32_from_utf8 (mp_cp);
}
if (m_edge == m_edge_end) {
if (c == '\012' || c == '\015') {
if (c == '\015' && m_string.size () > m_index + 1 && m_string [m_index] == '\012') {
++m_index;
}
++m_line;
if (m_line >= m_linestarts.size ()) {
m_linestarts.push_back (m_linestarts.back () + db::DVector (0.0, -(m_fp->ymax - m_fp->ymin + line_spacing)));
}
m_pos = m_linestarts [m_line];
if (c < m_fp->start_char || c >= m_fp->end_char) {
c = invalid_char;
}
++m_index;
if (c < m_fp->end_char && c >= m_fp->start_char) {
m_edge = m_fp->chars [c - m_fp->start_char].edge_start;
m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end;
m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0);
}
} else {
m_new_char = false;
}
}
while (m_line >= m_linestarts.size ()) {
db::DPoint last;
if (m_linestarts.size () > 0) {
last = m_linestarts.back ();
last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing));
}
m_linestarts.push_back (last);
}
if (!at_end ()) {
@ -316,16 +309,9 @@ basic_hershey_edge_iterator::get ()
void
basic_hershey_edge_iterator::inc ()
{
if (m_new_char) {
get ();
}
if (! at_end ()) {
++m_edge;
if (m_edge == m_edge_end) {
++m_index;
m_new_char = true;
}
get ();
}
}

View File

@ -51,11 +51,10 @@ public:
void inc ();
private:
bool m_new_char;
unsigned int m_line;
const char *mp_cp;
std::string m_string;
unsigned int m_edge, m_edge_end;
unsigned int m_index, m_end;
std::vector<db::DPoint> m_linestarts;
db::DPoint m_pos;
db::DVector m_delta;
@ -177,14 +176,16 @@ struct DB_PUBLIC_TEMPLATE hershey
/**
* @brief Obtain the size of the text
*
* @return The bounding box of the text with the scaling applied
* @return The bounding box of the text with the scaling and justification applied
*/
box<C> bbox () const
db::DBox bbox () const
{
db::DBox b = hershey_text_box (m_string, m_font);
db::point<C> p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale));
db::point<C> p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale));
return box<C> (p1, p2);
db::DBox b = hershey_text_box (m_string, m_font) * (1.0 / m_scale);
if (! m_linestarts.empty ()) {
return b.moved (m_linestarts.front () - db::DPoint ());
} else {
return b;
}
}
/**

View File

@ -791,35 +791,6 @@ Bitmap::render_contour (std::vector<lay::RenderEdge> &edges)
}
}
static unsigned char next_char_latin1_from_utf8 (const char *&cp, const char *cpf = 0)
{
unsigned char c = *cp;
if ((c & 0xe0) == 0xc0) {
if ((cp[1] & 0xc0) == 0x80 && (! cpf || cpf > cp + 1)) {
unsigned int x = ((unsigned int) ((unsigned char) c & 0x1f) << 6) | (unsigned int) (cp[1] & 0x3f);
cp += 1;
if (x < 255) {
c = x;
} else {
c = '?';
}
} else {
c = '?';
}
} else if ((c & 0xf0) == 0xe0) {
if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (! cpf || cpf > cp + 2)) {
cp += 2;
}
c = '?';
} else if ((c & 0xf8) == 0xf0) {
if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (cp[3] & 0xc0) == 0x80 && (! cpf || cpf > cp + 3)) {
cp += 3;
}
c = '?';
}
return c;
}
void
Bitmap::render_text (const lay::RenderText &text)
{
@ -830,12 +801,11 @@ Bitmap::render_text (const lay::RenderText &text)
// count the lines and max. characters per line
unsigned int lines = 1;
for (const char *cp = text.text.c_str (); *cp; ++cp) {
if (*cp == '\012' || *cp == '\015') {
if (*cp == '\015' && cp[1] == '\012') {
++cp;
}
for (const char *cp = text.text.c_str (); *cp; ) {
if (tl::skip_newline (cp)) {
++lines;
} else {
++cp;
}
}
@ -858,10 +828,9 @@ Bitmap::render_text (const lay::RenderText &text)
unsigned int length = 0;
const char *cp = cp1;
while (*cp && *cp != '\012' && *cp != '\015') {
next_char_latin1_from_utf8 (cp);
while (*cp && !tl::is_newline (*cp)) {
tl::utf32_from_utf8 (cp);
++length;
++cp;
}
double xx;
@ -880,11 +849,13 @@ Bitmap::render_text (const lay::RenderText &text)
for ( ; cp1 != cp; ++cp1) {
unsigned char c = next_char_latin1_from_utf8 (cp1, cp);
uint32_t c = tl::utf32_from_utf8 (cp1, cp);
if (c < uint32_t (ff.first_char ()) || c >= uint32_t (ff.n_chars ()) + ff.first_char ()) {
// NOTE: '?' needs to be a valid character always
c = uint32_t ('?');
}
size_t cc = c; // to suppress a compiler warning ..
if (c >= ff.first_char () && cc < size_t (ff.n_chars ()) + size_t (ff.first_char ())
&& xx > -100.0 && xx < double (width ())) {
if (xx > -100.0 && xx < double (width ())) {
fill_pattern (int (y + 0.5), int (floor (xx)), ff.data () + (c - ff.first_char ()) * ff.height () * ff.stride (), ff.stride (), ff.height ());
}
@ -897,11 +868,7 @@ Bitmap::render_text (const lay::RenderText &text)
}
// next line
if (*cp1 == '\012' || *cp1 == '\015') {
if (*cp1 == '\015' && cp1[1] == '\012') {
++cp1;
}
++cp1;
if (tl::skip_newline (cp1)) {
y -= double (ff.line_height ());
}

View File

@ -894,5 +894,5 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
}
} // namespace edt
} // namespace lay

View File

@ -320,6 +320,11 @@ public:
return m_line_styles;
}
/**
* @brief Reimplementation of ViewObjectCanvas: Resolution
*/
double resolution () const;
/**
* @brief Reimplementation of ViewObjectCanvas: Background color
*/
@ -444,8 +449,6 @@ private:
void do_redraw_all (bool force_redraw = true);
void prepare_drawing ();
virtual double resolution () const;
const std::vector<ViewOp> &scaled_view_ops (unsigned int lw);
};

View File

@ -1721,6 +1721,14 @@ public:
return mp_canvas;
}
/**
* @brief Gets the canvas object (const version)
*/
const lay::LayoutCanvas *canvas () const
{
return mp_canvas;
}
#if defined(HAVE_QT)
/**
* @brief Gets the layer control panel

View File

@ -0,0 +1,170 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2023 Matthias Koefferlein
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "layTextInfo.h"
#include "layFixedFont.h"
#include "layLayoutViewBase.h"
#include "layLayoutCanvas.h"
namespace lay
{
TextInfo::TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans)
: m_default_text_size (view->default_text_size ()),
m_default_font (db::Font (view->text_font ())),
m_apply_text_trans (view->apply_text_trans ()),
m_resolution (view->canvas ()->resolution ()),
m_vp_trans (vp_trans)
{
// .. nothing yet ..
}
TextInfo::TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution, const db::DCplxTrans &vp_trans)
: m_default_text_size (default_text_size),
m_default_font (default_font),
m_apply_text_trans (apply_text_trans),
m_resolution (resolution),
m_vp_trans (vp_trans)
{
// .. nothing yet ..
}
db::DBox
TextInfo::get_bbox (const db::DText &text) const
{
// offset in pixels (space between origin and text)
const double offset = 2.0;
db::DFTrans fp (db::DFTrans::r0);
db::DCoord h;
db::Font font = text.font () == db::NoFont ? m_default_font : text.font ();
if (m_apply_text_trans && font != db::NoFont && font != db::DefaultFont) {
fp = db::DFTrans (m_vp_trans.fp_trans () * text.trans ().fp_trans ());
h = m_vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size);
} else {
h = m_vp_trans.ctrans (m_default_text_size);
}
db::HAlign halign = text.halign ();
db::VAlign valign = text.valign ();
double fy = 0.0;
if (valign == db::VAlignBottom || valign == db::NoVAlign) {
fy = 1.0;
} else if (valign == db::VAlignTop) {
fy = -1.0;
}
double fx = 0.0;
if (halign == db::HAlignLeft || halign == db::NoHAlign) {
fx = 1.0;
} else if (halign == db::HAlignRight) {
fx = -1.0;
}
db::DVector tp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h);
db::DVector tp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h);
db::DPoint dp = db::DPoint () + text.trans ().disp ();
db::DBox b (dp + fp (tp1), dp + fp (tp2));
if (font == db::DefaultFont) {
const lay::FixedFont &ff = lay::FixedFont::get_font (m_resolution);
// count the lines
unsigned int lines = 1;
for (const char *cp = text.string (); *cp; ) {
if (tl::skip_newline (cp)) {
++lines;
} else {
tl::utf32_from_utf8 (cp);
}
}
// compute the actual top left position
double ytop;
if (valign == db::VAlignBottom || valign == db::NoVAlign) {
ytop = b.bottom ();
ytop += double (ff.line_height () * (lines - 1) + ff.height ());
} else if (valign == db::VAlignCenter) {
ytop = b.center ().y ();
ytop += double ((ff.line_height () * (lines - 1) + ff.height ()) / 2);
} else {
ytop = b.top ();
}
// compute the bottom position
double ybottom = ytop - ff.line_height () * (lines - 1);
// left and right position
bool first = false;
double xleft = 0.0, xright = 0.0;
const char *cp = text.string ();
while (*cp) {
unsigned int length = 0;
while (*cp && !tl::skip_newline (cp)) {
tl::utf32_from_utf8 (cp);
++length;
}
double xl;
if (halign == db::HAlignRight) {
xl = b.right ();
xl -= double (ff.width () * length);
} else if (halign == db::HAlignCenter) {
xl = b.center ().x ();
xl -= double (ff.width () * length / 2);
} else {
xl = b.left ();
}
xl -= 0.5;
double xr = xl + double (ff.width () * length);
if (first || xl < xleft) {
xleft = xl;
}
if (first || xr > xright) {
xright = xr;
}
first = false;
}
return db::DBox (xleft, ybottom, xright, ytop).transformed (m_vp_trans.inverted ());
} else {
db::DHershey ht (text.string (), font);
ht.justify (b.transformed (m_vp_trans.inverted ()), halign, valign);
return ht.bbox ();
}
}
}

View File

@ -0,0 +1,89 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2023 Matthias Koefferlein
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_layTextInfo
#define HDR_layTextInfo
#include "laybasicCommon.h"
#include "dbText.h"
#include "dbBox.h"
namespace lay
{
class LayoutViewBase;
/**
* @brief A class providing information about a text's visual bounding box
*
* The class can act as a BoxConverter.
*/
class LAYBASIC_PUBLIC TextInfo
{
public:
/**
* @brief Constructor
*
* @param view The LayoutView from which to take the text display parameters
* @param vp_trans The effective micron-to-pixel transformation
*/
TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans);
/**
* @brief Constructor
*
* @param default_text_size The default text size in micron
* @param default_font The default font
* @param apply_text_trans True if text transformations are to be applied
* @param resolution The resolution value (logical pixel size per physical unit pixel)
* @param vp_trans The effective micron-to-pixel transformation
*/
TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution, const db::DCplxTrans &vp_trans);
/**
* @brief Gets the visual bounding box of the given DText object
*
* The visual bounding box is returned in micrometer units.
* It encloses the glyphs of the text, taking into account the
* text view settings by the view.
*/
db::DBox operator() (const db::DText &text) const
{
return get_bbox (text);
}
private:
double m_default_text_size;
db::Font m_default_font;
bool m_apply_text_trans;
double m_resolution;
db::DCplxTrans m_vp_trans;
db::DBox get_bbox (const db::DText &text) const;
};
}
#endif

View File

@ -52,6 +52,7 @@ SOURCES += \
layEditable.cc \
layEditorServiceBase.cc \
layFinder.cc \
layTextInfo.cc \
layFixedFont.cc \
layLayoutCanvas.cc \
layLineStylePalette.cc \
@ -104,6 +105,7 @@ HEADERS += \
layEditorServiceBase.h \
layLayoutCanvas.h \
layFinder.h \
layTextInfo.h \
layFixedFont.h \
layLayoutViewBase.h \
layLineStylePalette.h \

View File

@ -210,6 +210,22 @@ inline bool safe_isspace (char c)
return c != 0 && static_cast<unsigned char> (c) < 0x80 && isspace (c);
}
// -------------------------------------------------------------------------
// Utility: skip a newline
bool skip_newline (const char *&cp)
{
if (*cp == '\012' || *cp == '\015') {
if (*cp == '\015' && cp[1] == '\012') {
++cp;
}
++cp;
return true;
} else {
return false;
}
}
// -------------------------------------------------------------------------
// Utility: a strtod version that is independent of the locale

View File

@ -987,6 +987,37 @@ TL_PUBLIC uint32_t utf32_upcase (uint32_t c32);
*/
TL_PUBLIC uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0);
/**
* @brief Checks if the next characters are CR, LF or CR+LF and skips them
*
* This function returns true, if a line separated was found and skipped
*/
TL_PUBLIC bool skip_newline (const char *&cp);
/**
* @brief checks if the given character is a CR character
*/
inline bool is_cr (char c)
{
return c == '\015';
}
/**
* @brief checks if the given character is a LF character
*/
inline bool is_lf (char c)
{
return c == '\012';
}
/**
* @brief checks if the given character is a CR or LF character
*/
inline bool is_newline (char c)
{
return is_cr (c) || is_lf (c);
}
} // namespace tl
#endif