@@ -1091,7 +1091,7 @@ void OcdFileExport::exportSymbols(OcdFile<Format>& file)
10911091
10921092 case Symbol::NoSymbol:
10931093 case Symbol::AllSymbols:
1094- Q_UNREACHABLE ();
1094+ FILEFORMAT_ASSERT ( false ); // unreachable
10951095 }
10961096
10971097 FILEFORMAT_ASSERT (!ocd_symbol.isEmpty ());
@@ -1331,7 +1331,7 @@ qint16 OcdFileExport::exportSubPattern(const MapCoordVector& coords, const Symbo
13311331 case Symbol::AllSymbols:
13321332 case Symbol::Combined:
13331333 case Symbol::Text:
1334- Q_UNREACHABLE ();
1334+ FILEFORMAT_ASSERT ( false ); // unreachable
13351335 }
13361336
13371337 return num_coords;
@@ -1976,14 +1976,145 @@ void OcdFileExport::setupTextSymbolFraming(const TextSymbol* text_symbol, OcdTex
19761976
19771977
19781978
1979+ /* *
1980+ * Returns the type of symbol which would be constructed by exportCombinedSymbol().
1981+ */
1982+ int OcdFileExport::checkCombinedSymbol (const CombinedSymbol* combined_symbol) const
1983+ {
1984+ // The implementation must mirror exportCombinedSymbol()!
1985+
1986+ auto num_parts = 0 ; // The count of non-null parts.
1987+ const Symbol* parts[3 ] = {}; // A random access list without holes
1988+ for (auto i = 0 ; i < combined_symbol->getNumParts (); ++i)
1989+ {
1990+ if (auto const * part = combined_symbol->getPart (i))
1991+ {
1992+ if (num_parts < 3 )
1993+ parts[num_parts] = part;
1994+ ++num_parts;
1995+ }
1996+ }
1997+
1998+ switch (num_parts)
1999+ {
2000+ case 1 :
2001+ // Single subsymbol: Output just this subsymbol, if sufficient.
2002+ switch (combined_symbol->getType ())
2003+ {
2004+ case Symbol::Area:
2005+ return Ocd::SymbolTypeArea;
2006+ case Symbol::Line:
2007+ return Ocd::SymbolTypeLine;
2008+ case Symbol::Combined:
2009+ return 99 ;
2010+ case Symbol::Point:
2011+ case Symbol::Text:
2012+ case Symbol::NoSymbol:
2013+ case Symbol::AllSymbols:
2014+ return 99 ;
2015+ }
2016+ break ;
2017+
2018+ case 2 :
2019+ // Two subsymbols: Area with border, or line with framing, if sufficient.
2020+ if (parts[1 ]->getType () == Symbol::Area)
2021+ {
2022+ std::swap (parts[0 ], parts[1 ]);
2023+ }
2024+
2025+ if (parts[0 ]->getType () == Symbol::Area)
2026+ {
2027+ if (ocd_version < 9 )
2028+ break ;
2029+
2030+ // Area symbol with border, since OCD V9
2031+ auto const exported_as_line = [this ](Symbol const * symbol) -> bool {
2032+ auto const type = symbol->getType ();
2033+ return type == Symbol::Line
2034+ || (type == Symbol::Combined
2035+ && checkCombinedSymbol (static_cast <CombinedSymbol const *>(symbol)) == Ocd::SymbolTypeLine);
2036+ };
2037+
2038+ auto const * border_symbol = parts[1 ];
2039+ if (!exported_as_line (border_symbol))
2040+ {
2041+ // Not a suitable border line symbol
2042+ break ;
2043+ }
2044+ else
2045+ {
2046+ // Same result for different branches in exportCombinedSymbol.
2047+ return Ocd::SymbolTypeArea;
2048+ }
2049+ }
2050+ Q_FALLTHROUGH ();
2051+
2052+ case 3 :
2053+ // Three subsymbols: Line with framing line and filled double line, if sufficient.
2054+ if (parts[0 ]->getType () == Symbol::Line && parts[1 ]->getType () == Symbol::Line
2055+ && (num_parts == 2 || parts[2 ]->getType () == Symbol::Line))
2056+ {
2057+ // Complex line symbol
2058+ // Desired assignment, after rearrangement
2059+ auto main_line = static_cast <const LineSymbol*>(parts[0 ]);
2060+ auto framing = static_cast <const LineSymbol*>(parts[1 ]);
2061+ auto double_line = static_cast <const LineSymbol*>(parts[2 ]);
2062+ if (!maybeDoubleFilling (double_line))
2063+ {
2064+ // Select candidate double line/filling
2065+ if (maybeDoubleFilling (main_line))
2066+ std::swap (main_line, double_line);
2067+ else if (maybeDoubleFilling (framing))
2068+ std::swap (framing, double_line);
2069+ else if (double_line)
2070+ break ;
2071+ }
2072+ if (!maybeFraming (framing))
2073+ {
2074+ // Select candidate framing
2075+ if (!main_line || maybeFraming (main_line))
2076+ std::swap (main_line, framing);
2077+ else if (framing)
2078+ break ;
2079+ }
2080+ if (!maybeMainLine (main_line))
2081+ {
2082+ if (main_line)
2083+ break ;
2084+ std::swap (main_line, framing);
2085+ }
2086+
2087+ return Ocd::SymbolTypeLine;
2088+ }
2089+ break ;
2090+
2091+ default :
2092+ break ;
2093+ }
2094+
2095+ // Fallback
2096+ return 99 ;
2097+ }
2098+
2099+
2100+ // The behaviour of this function must be mirrored by checkCombinedSymbol().
19792101template < class Format >
19802102void OcdFileExport::exportCombinedSymbol (OcdFile<Format>& file, const CombinedSymbol* combined_symbol)
19812103{
2104+ // Creates a breakdown record for combined symbols which are mapped to
2105+ // a simple OCD symbol. This record is needed to handle path objects
2106+ // which use such symbols.
2107+ auto const add_breakdown = [this ](quint32 symbol_number, quint8 type) {
2108+ breakdown_index[symbol_number] = breakdown_list.size ();
2109+ breakdown_list.push_back ({symbol_number, type});
2110+ breakdown_list.push_back ({0 , 0 });
2111+ };
2112+
19822113 auto num_parts = 0 ; // The count of non-null parts.
19832114 const Symbol* parts[3 ] = {}; // A random access list without holes
19842115 for (auto i = 0 ; i < combined_symbol->getNumParts (); ++i)
19852116 {
1986- if (auto part = combined_symbol->getPart (i))
2117+ if (auto const * part = combined_symbol->getPart (i))
19872118 {
19882119 if (num_parts < 3 )
19892120 parts[num_parts] = part;
@@ -2004,6 +2135,7 @@ void OcdFileExport::exportCombinedSymbol(OcdFile<Format>& file, const CombinedSy
20042135 copySymbolHead (*combined_symbol, *copy);
20052136 auto ocd_subsymbol = exportAreaSymbol<typename Format::AreaSymbol>(copy.get (), symbol_number);
20062137 file.symbols ().insert (ocd_subsymbol);
2138+ add_breakdown (symbol_number, Ocd::SymbolTypeArea);
20072139 }
20082140 return ;
20092141 case Symbol::Line:
@@ -2012,6 +2144,7 @@ void OcdFileExport::exportCombinedSymbol(OcdFile<Format>& file, const CombinedSy
20122144 copySymbolHead (*combined_symbol, *copy);
20132145 auto ocd_subsymbol = exportLineSymbol<typename Format::LineSymbol>(copy.get (), symbol_number);
20142146 file.symbols ().insert (ocd_subsymbol);
2147+ add_breakdown (symbol_number, Ocd::SymbolTypeLine);
20152148 }
20162149 return ;
20172150 case Symbol::Combined:
@@ -2020,50 +2153,79 @@ void OcdFileExport::exportCombinedSymbol(OcdFile<Format>& file, const CombinedSy
20202153 case Symbol::Text:
20212154 case Symbol::NoSymbol:
20222155 case Symbol::AllSymbols:
2023- Q_UNREACHABLE ();
2156+ FILEFORMAT_ASSERT ( false ); // unreachable
20242157 }
20252158 break ;
20262159
20272160 case 2 :
20282161 // Two subsymbols: Area with border, or line with framing, if sufficient.
2029- case 3 :
2030- // Three subsymbols: Line with framing line and filled double line, if sufficient.
2031- if (parts[0 ]->getType () != Symbol::Line && parts[1 ]->getType () != Symbol::Line)
2032- {
2033- break ;
2034- }
2035-
20362162 if (parts[1 ]->getType () == Symbol::Area)
20372163 {
20382164 std::swap (parts[0 ], parts[1 ]);
20392165 }
20402166
20412167 if (parts[0 ]->getType () == Symbol::Area)
20422168 {
2043- if (ocd_version < 9 || num_parts != 2 )
2169+ if (ocd_version < 9 )
20442170 break ;
20452171
20462172 // Area symbol with border, since OCD V9
2047- auto border_symbol = static_cast <const LineSymbol*>(parts[1 ]);
2048- if (symbol_numbers.find (border_symbol) == end (symbol_numbers))
2173+ auto const exported_as_line = [this ](Symbol const * symbol) -> bool {
2174+ auto const type = symbol->getType ();
2175+ return type == Symbol::Line
2176+ || (type == Symbol::Combined
2177+ && checkCombinedSymbol (static_cast <CombinedSymbol const *>(symbol)) == Ocd::SymbolTypeLine);
2178+ };
2179+
2180+ auto const * border_symbol = parts[1 ];
2181+ if (!exported_as_line (border_symbol))
2182+ {
2183+ // Not a suitable border line symbol
2184+ break ;
2185+ }
2186+ else if (symbol_numbers.find (border_symbol) != end (symbol_numbers))
20492187 {
2050- // An unknown border symbol must be a private one
2188+ // The border line is a regular symbol.
2189+ }
2190+ else if (border_symbol->getType () == Symbol::Line)
2191+ {
2192+ // The border line is a private LineSymbol.
20512193 auto border_duplicate = duplicate (static_cast <const LineSymbol&>(*border_symbol));
20522194 copySymbolHead (*combined_symbol, *border_duplicate);
2053- border_duplicate->setName (QLatin1String (" Border of " ) + border_symbol->getName ());
2195+ border_duplicate->setName (QLatin1String (" Border of " ) + combined_symbol->getName ());
2196+ auto const border_symbol_number = makeUniqueSymbolNumber (symbol_number);
2197+ symbol_numbers[border_duplicate.get ()] = border_symbol_number;
2198+ file.symbols ().insert (exportLineSymbol<typename Format::LineSymbol>(border_duplicate.get (), border_symbol_number));
20542199 border_symbol = border_duplicate.get ();
20552200 temporary_symbols.emplace_back (std::move (border_duplicate));
2056- auto border_symbol_number = makeUniqueSymbolNumber (symbol_number);
2057- symbol_numbers[border_symbol] = border_symbol_number;
2058- file.symbols ().insert (exportLineSymbol<typename Format::LineSymbol>(border_symbol, border_symbol_number));
2201+ }
2202+ else if (border_symbol->getType () == Symbol::Combined)
2203+ {
2204+ // The border line is a private CombinedSymbol, exported as OCD line symbol.
2205+ auto border_duplicate = duplicate (static_cast <const CombinedSymbol&>(*border_symbol));
2206+ copySymbolHead (*combined_symbol, *border_duplicate);
2207+ border_duplicate->setName (QLatin1String (" Border of " ) + combined_symbol->getName ());
2208+ auto const border_symbol_number = makeUniqueSymbolNumber (symbol_number);
2209+ symbol_numbers[border_duplicate.get ()] = border_symbol_number;
2210+ exportCombinedSymbol<Format>(file, border_duplicate.get ());
2211+ border_symbol = border_duplicate.get ();
2212+ temporary_symbols.emplace_back (std::move (border_duplicate));
2213+ }
2214+ else
2215+ {
2216+ FILEFORMAT_ASSERT (false ); // unreachable
20592217 }
20602218
20612219 auto copy = duplicate (static_cast <const AreaSymbol&>(*parts[0 ]));
20622220 copySymbolHead (*combined_symbol, *copy);
20632221 file.symbols ().insert (exportCombinedAreaSymbol<typename Format::AreaSymbol>(symbol_number, combined_symbol, copy.get (), border_symbol));
2222+ add_breakdown (symbol_number, Ocd::SymbolTypeArea);
20642223 return ;
20652224 }
2225+ Q_FALLTHROUGH ();
20662226
2227+ case 3 :
2228+ // (Up to) three subsymbols: Line with framing line and filled double line, if sufficient.
20672229 if (parts[0 ]->getType () == Symbol::Line && parts[1 ]->getType () == Symbol::Line
20682230 && (num_parts == 2 || parts[2 ]->getType () == Symbol::Line))
20692231 {
@@ -2101,6 +2263,7 @@ void OcdFileExport::exportCombinedSymbol(OcdFile<Format>& file, const CombinedSy
21012263 auto copy = duplicate (static_cast <const LineSymbol&>(*main_line));
21022264 copySymbolHead (*combined_symbol, *copy);
21032265 file.symbols ().insert (exportCombinedLineSymbol<typename Format::LineSymbol>(symbol_number, combined_symbol, copy.get (), framing, double_line));
2266+ add_breakdown (symbol_number, Ocd::SymbolTypeLine);
21042267 return ;
21052268 }
21062269 break ;
@@ -2152,7 +2315,7 @@ void OcdFileExport::exportGenericCombinedSymbol(OcdFile<Format>& file, const Com
21522315 break ;
21532316 case Symbol::NoSymbol:
21542317 case Symbol::AllSymbols:
2155- Q_UNREACHABLE ();
2318+ FILEFORMAT_ASSERT ( false ); // unreachable
21562319 }
21572320 if (type == 0 )
21582321 {
@@ -2188,9 +2351,9 @@ QByteArray OcdFileExport::exportCombinedAreaSymbol<Ocd::AreaSymbolV8>(
21882351 quint32 /* symbol_number*/ ,
21892352 const CombinedSymbol* /* combined_symbol*/ ,
21902353 const AreaSymbol* /* area_symbol*/ ,
2191- const LineSymbol * /* line_symbol*/ )
2354+ const Symbol * /* line_symbol*/ )
21922355{
2193- Q_UNREACHABLE ();
2356+ FILEFORMAT_ASSERT ( false ); // unreachable
21942357}
21952358
21962359
@@ -2199,7 +2362,7 @@ QByteArray OcdFileExport::exportCombinedAreaSymbol(
21992362 quint32 symbol_number,
22002363 const CombinedSymbol* combined_symbol,
22012364 const AreaSymbol* area_symbol,
2202- const LineSymbol * line_symbol )
2365+ const Symbol * line_symbol )
22032366{
22042367 auto ocd_symbol = exportAreaSymbol<OcdAreaSymbol>(area_symbol, symbol_number);
22052368 auto ocd_subsymbol_data = reinterpret_cast <OcdAreaSymbol*>(ocd_symbol.data ());
0 commit comments