• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "print_cups_ppd.h"
17 #include "print_service_converter.h"
18 #include "print_log.h"
19 
20 
21 namespace OHOS::Print {
22 using namespace std;
23 
24 const std::vector<std::string> baseOptionStr = {
25     "PageSize",
26     "PageRegion",
27     "MediaType",
28     "ColorModel",
29     "OutputMode",
30     "cupsPrintQuality",
31     "Duplex",
32     "JCLDuplex",
33     "EFDuplex",
34     "EFDuplexing",
35     "KD03Duplex",
36     "Collate",
37     "OutputOrder",
38     "CNGrayscale",
39     "BRMonoColor"
40 };
41 
GetCNFromPpdAttr(ppd_file_t * ppd,const char * keyword,const char * choice,const char * defaultText)42 const char *GetCNFromPpdAttr(ppd_file_t *ppd, const char *keyword, const char* choice, const char* defaultText)
43 {
44     ppd_attr_t *locattrCN = _ppdLocalizedAttr(ppd, keyword, choice, "zh_CN");
45     if (!locattrCN) {
46         return defaultText;
47     }
48 
49     return locattrCN->text;
50 }
51 
GetAdvanceOptJsSingleJSFromOption(ppd_file_t * ppd,ppd_option_t * opt,Json::Value & advanceOptJsSingle)52 void GetAdvanceOptJsSingleJSFromOption(ppd_file_t *ppd, ppd_option_t *opt, Json::Value& advanceOptJsSingle)
53 {
54     Json::Value advanceChoiceJs;
55     Json::Value advanceChoiceJsDefaultLanguage;
56     Json::Value advanceChoiceJsCNLanguage;
57     Json::Value advanceOptionTextJs;
58 
59     PRINT_HILOGI("option=%{public}s default=%{public}s text=%{public}s\n", opt->keyword, opt->defchoice, opt->text);
60     ppd_choice_t* choices = opt->choices;
61     for (int k = 0; k < opt->num_choices; k++) {
62         if (choices + k == nullptr) {
63             PRINT_HILOGE("PPD choice found error: %{public}s", opt->keyword);
64             break;
65         }
66         if (!strcmp(choices[k].choice, "Custom") && ppdFindCustomOption(ppd, opt->keyword)) {
67             PRINT_HILOGI("Ignore Custom PPD Choice: %{public}s", opt->keyword);
68             continue;
69         }
70         advanceChoiceJsDefaultLanguage[choices[k].choice] = choices[k].text;
71         advanceChoiceJsCNLanguage[choices[k].choice] = GetCNFromPpdAttr(ppd, opt->keyword, choices[k].choice,
72             choices[k].text);
73     }
74     advanceChoiceJs["default"] = advanceChoiceJsDefaultLanguage;
75     advanceChoiceJs["zh_CN"] = advanceChoiceJsCNLanguage;
76     advanceOptionTextJs["default"] = opt->text;
77     advanceOptionTextJs["zh_CN"] = GetCNFromPpdAttr(ppd, "Translation", opt->keyword, opt->text);
78     advanceOptJsSingle["choice"] = advanceChoiceJs;
79     advanceOptJsSingle["keyword"] = opt->keyword;
80     advanceOptJsSingle["optionText"] = advanceOptionTextJs;
81     advanceOptJsSingle["uiType"] = opt->ui;
82 }
83 
GetAdvanceOptionsFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)84 void GetAdvanceOptionsFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
85 {
86     cups_array_t *options_arr = ppd->options;
87     Json::Value advanceOptJs;
88     Json::Value advanceDefaultJs;
89     for (ppd_option_t *opt = (ppd_option_t*)cupsArrayFirst(options_arr);
90         opt != nullptr; opt = (ppd_option_t*)cupsArrayNext(options_arr)) {
91         if (advanceOptJs.size() > ADVANCE_OPTION_MAXLENGTH) {
92             PRINT_HILOGE("AdvanceOption size is over max length!");
93             break;
94         }
95         if (std::find(baseOptionStr.begin(), baseOptionStr.end(), string(opt->keyword)) != baseOptionStr.end()) {
96             continue;
97         }
98         if (opt->ui == PPD_UI_PICKMANY) {
99             PRINT_HILOGW("PPD_UI_PICKMANY Found: %{public}s", opt->keyword);
100             continue;
101         }
102         advanceDefaultJs[opt->keyword] = opt->defchoice;
103         Json::Value advanceOptJsSingle;
104         GetAdvanceOptJsSingleJSFromOption(ppd, opt, advanceOptJsSingle);
105         advanceOptJs.append(advanceOptJsSingle);
106     }
107     printerCaps.SetPrinterAttrNameAndValue("advanceOptions", PrintJsonUtil::WriteString(advanceOptJs).c_str());
108     printerCaps.SetPrinterAttrNameAndValue("advanceDefault", PrintJsonUtil::WriteString(advanceDefaultJs).c_str());
109 }
110 
QueryPrinterCapabilityFromPPDFile(PrinterCapability & printerCaps,const std::string & ppdFilePath)111 int32_t QueryPrinterCapabilityFromPPDFile(PrinterCapability &printerCaps, const std::string &ppdFilePath)
112 {
113     PRINT_HILOGI("QueryPrinterCapabilityFromPPDFile start %{private}s", ppdFilePath.c_str());
114     char *locale = setlocale(LC_ALL, "zh_CN.UTF-8");
115     if (locale == nullptr) {
116         PRINT_HILOGE("setlocale fail");
117         return E_PRINT_FILE_IO;
118     }
119     ppd_file_t *ppd = ppdOpenFile(ppdFilePath.c_str());
120     if (ppd == nullptr) {
121         PRINT_HILOGE("Open PPD File fail");
122         return E_PRINT_FILE_IO;
123     }
124 
125     ppd->cache = _ppdCacheCreateWithPPD(ppd);
126     if (ppd->cache == nullptr) {
127         PRINT_HILOGE("PPDCacheCreateWithPPD fail");
128         return E_PRINT_FILE_IO;
129     }
130     GetAdvanceOptionsFromPPD(ppd, printerCaps);
131     ParsePrinterAttributesFromPPD(ppd, printerCaps);
132 
133     ppdClose(ppd);
134     return E_PRINT_NONE;
135 }
136 
ParsePageSizeAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)137 void ParsePageSizeAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
138 {
139     Json::Value mediaSizeMap;
140     Json::Value mediaSizeMapDefaultLanguage;
141     Json::Value mediaSizeMapCNLanguage;
142     _ppd_cache_t *ppdCache = ppd->cache;
143     pwg_size_t *cacheSizes = ppdCache->sizes;
144     if (cacheSizes == nullptr) {
145         PRINT_HILOGE("cacheSizes get nullptr!");
146         return;
147     }
148     std::vector<PrintPageSize> supportedPageSizes;
149     ppd_option_t *sizeOption = ppdFindOption(ppd, "PageSize");
150 
151     for (int i = 0; i < ppdCache->num_sizes; i++) {
152         pwg_size_t pwgSize = cacheSizes[i];
153         ppd_choice_t *sizeChoice = ppdFindChoice(sizeOption, pwgSize.map.ppd);
154         if (sizeChoice == nullptr) {
155             PRINT_HILOGE("sizeChoice get nullptr!");
156             return;
157         }
158         std::string id = PrintPageSize::MatchPageSize(pwgSize.map.pwg);
159         PrintPageSize dst;
160         if (!id.empty()) {
161             PrintPageSize::FindPageSizeById(id, dst);
162         } else {
163             dst = PrintPageSize(sizeChoice->text, pwgSize.map.pwg,
164                 round(pwgSize.width * HUNDRED_OF_MILLIMETRE_TO_INCH / ONE_HUNDRED),
165                 round(pwgSize.length * HUNDRED_OF_MILLIMETRE_TO_INCH / ONE_HUNDRED));
166         }
167         supportedPageSizes.emplace_back(dst);
168 
169         mediaSizeMapDefaultLanguage[pwgSize.map.pwg] = sizeChoice->text;
170         mediaSizeMapCNLanguage[pwgSize.map.pwg] = GetCNFromPpdAttr(ppd, "PageSize",
171             sizeChoice->choice, sizeChoice->text);
172     }
173     mediaSizeMap["default"] = mediaSizeMapDefaultLanguage;
174     mediaSizeMap["zh_CN"] = mediaSizeMapCNLanguage;
175     printerCaps.SetSupportedPageSize(supportedPageSizes);
176     printerCaps.SetPrinterAttrNameAndValue("mediaSizeMap", PrintJsonUtil::WriteString(mediaSizeMap).c_str());
177 
178     // find default media size
179     if (sizeOption && sizeOption->defchoice) {
180         pwg_size_t *pwgSize = _ppdCacheGetSize(ppdCache, sizeOption->defchoice);
181         PRINT_HILOGI("Default page size: %{public}s, defchoice : %{public}s", pwgSize->map.ppd, sizeOption->defchoice);
182         std::string defaultPageSizeId;
183         if (!ConvertPageSizeId(pwgSize->map.pwg, defaultPageSizeId)) {
184             defaultPageSizeId = std::string(pwgSize->map.ppd);
185         }
186         printerCaps.SetPrinterAttrNameAndValue("defaultPageSizeId", defaultPageSizeId.c_str());
187     } else {
188         PRINT_HILOGE("Default page size: None");
189     }
190 }
191 
ParseDuplexModeAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)192 void ParseDuplexModeAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
193 {
194     _ppd_cache_t *ppdCache = ppd->cache;
195     std::vector<DuplexModeCode> supportedDuplexModeList = { DUPLEX_MODE_ONE_SIDED };
196     if (ppdCache->sides_2sided_long) {
197         supportedDuplexModeList.emplace_back(DUPLEX_MODE_TWO_SIDED_LONG_EDGE);
198     }
199     if (ppdCache->sides_2sided_short) {
200         supportedDuplexModeList.emplace_back(DUPLEX_MODE_TWO_SIDED_SHORT_EDGE);
201     }
202     printerCaps.SetSupportedDuplexMode(std::vector<uint32_t>(supportedDuplexModeList.begin(),
203         supportedDuplexModeList.end()));
204     if (static_cast<int>(supportedDuplexModeList.size()) <= 1) {
205         printerCaps.SetDuplexMode((uint32_t)DUPLEX_MODE_ONE_SIDED);
206     } else {
207         printerCaps.SetDuplexMode((uint32_t)DUPLEX_MODE_TWO_SIDED_LONG_EDGE);
208     }
209     std::string duplexModeJson = ConvertListToJson<DuplexModeCode>(supportedDuplexModeList, ConvertDuplexModeToJson);
210     printerCaps.SetPrinterAttrNameAndValue("sides-supported", duplexModeJson.c_str());
211     // find default duplex mode
212     ppd_option_t *duplex = nullptr;
213     DuplexModeCode code = DUPLEX_MODE_ONE_SIDED;
214     const std::vector<std::string> duplexOptions = { "Duplex", "EFDuplex", "EFDuplexing", "KD03Duplex", "JCLDuplex" };
215     for (const auto& option : duplexOptions) {
216         duplex = ppdFindOption(ppd, option.c_str());
217         if (duplex != nullptr) {
218             break;
219         }
220     }
221     if (duplex && duplex->num_choices > 1) {
222         if (!strcmp(duplex->defchoice, "DuplexTumble"))
223             code = DUPLEX_MODE_TWO_SIDED_SHORT_EDGE;
224         else if (!strcmp(duplex->defchoice, "DuplexNoTumble"))
225             code = DUPLEX_MODE_TWO_SIDED_LONG_EDGE;
226     }
227     uint32_t value = static_cast<uint32_t>(code);
228     printerCaps.SetPrinterAttrNameAndValue("sides-default", std::to_string(value).c_str());
229 }
230 
ParseMediaTypeAttributeFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)231 void ParseMediaTypeAttributeFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
232 {
233     ppd_option_t *typeOption = ppdFindOption(ppd, "MediaType");
234     if (typeOption == nullptr) {
235         PRINT_HILOGE("ppdFindOption MediaType fail!");
236         return;
237     }
238     _ppd_cache_t *ppdCache = ppd->cache;
239     Json::Value jsonArray;
240     Json::Value jsonArrayDefaultLanguage;
241     Json::Value jsonArrayCNLanguage;
242 
243     // compatible with Spooler USB
244     Json::Value jsonArrayOld;
245 
246     std::vector<std::string> supportedMediaTypeList;
247     for (int i = 0; i < ppdCache->num_types; i++) {
248         if (ppdCache->types + i == nullptr) {
249             PRINT_HILOGE("ppdCache types is nullptr!");
250             return;
251         }
252         ppd_choice_t *typeChoice = ppdFindChoice(typeOption, ppdCache->types[i].ppd);
253         if (typeChoice == nullptr) {
254             PRINT_HILOGE("typeChoice is nullptr!");
255             return;
256         }
257         jsonArrayDefaultLanguage[ppdCache->types[i].pwg] = typeChoice->text;
258         jsonArrayCNLanguage[ppdCache->types[i].pwg] = GetCNFromPpdAttr(ppd, "MediaType",
259             typeChoice->choice, typeChoice->text);
260         // compatible with Spooler USB
261         jsonArrayOld.append(ppdCache->types[i].pwg);
262 
263         supportedMediaTypeList.emplace_back(std::string(ppdCache->types[i].pwg));
264     }
265     jsonArray["default"] = jsonArrayDefaultLanguage;
266     jsonArray["zh_CN"] = jsonArrayCNLanguage;
267     printerCaps.SetSupportedMediaType(supportedMediaTypeList);
268 
269     if (!supportedMediaTypeList.empty()) {
270         printerCaps.SetPrinterAttrNameAndValue("mediaTypeMap", PrintJsonUtil::WriteString(jsonArray).c_str());
271         // compatible with Spooler USB
272         printerCaps.SetPrinterAttrNameAndValue("media-type-supported",
273             PrintJsonUtil::WriteString(jsonArrayOld).c_str());
274     }
275 
276     if (typeOption->defchoice == nullptr) {
277         PRINT_HILOGE("typeOption->defchoice fail!");
278         return;
279     }
280     const char *typeDefault = _ppdCacheGetType(ppdCache, typeOption->defchoice);
281     if (typeDefault == nullptr) {
282         PRINT_HILOGE("_ppdCacheGetType typeDefault fail! typeOption: %{public}s", typeOption->defchoice);
283         return;
284     }
285     printerCaps.SetPrinterAttrNameAndValue("media-type-default", typeDefault);
286 }
287 
ParseColorModeAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)288 void ParseColorModeAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
289 {
290     std::vector<ColorModeCode> supportedColorModes = { ColorModeCode::COLOR_MODE_MONOCHROME };
291     ColorModeCode defaultColor = ColorModeCode::COLOR_MODE_MONOCHROME;
292     printerCaps.SetColorMode(ColorModeCode::COLOR_MODE_MONOCHROME);
293     if (ppd->color_device) {
294         printerCaps.SetColorMode(ColorModeCode::COLOR_MODE_COLOR);
295         supportedColorModes.emplace_back(ColorModeCode::COLOR_MODE_COLOR);
296         defaultColor = ColorModeCode::COLOR_MODE_COLOR;
297     }
298     printerCaps.SetSupportedColorMode(std::vector<uint32_t>(supportedColorModes.begin(), supportedColorModes.end()));
299 
300     std::string keyword = "print-color-mode-supported";
301     std::string colorModeJson = ConvertListToJson<ColorModeCode>(supportedColorModes, ConvertColorModeToJson);
302     printerCaps.SetPrinterAttrNameAndValue(keyword.c_str(), colorModeJson.c_str());
303 
304     uint32_t mode = static_cast<uint32_t>(defaultColor);
305     printerCaps.SetPrinterAttrNameAndValue("defaultColorMode", std::to_string(mode).c_str());
306 }
307 
308 
ParseQualityAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)309 void ParseQualityAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
310 {
311     std::vector<PrintQualityCode> supportedQualitiesList;
312     ppd_option_t *outputMode = ppdFindOption(ppd, "OutputMode");
313     if (!outputMode) {
314         outputMode = ppdFindOption(ppd, "cupsPrintQuality");
315     }
316     if (outputMode) {
317         if (ppdFindChoice(outputMode, "draft") || ppdFindChoice(outputMode, "fast")) {
318             supportedQualitiesList.emplace_back(PrintQualityCode::PRINT_QUALITY_DRAFT);
319         }
320         supportedQualitiesList.emplace_back(PrintQualityCode::PRINT_QUALITY_NORMAL);
321         if (ppdFindChoice(outputMode, "best") || ppdFindChoice(outputMode, "high")) {
322             supportedQualitiesList.emplace_back(PrintQualityCode::PRINT_QUALITY_HIGH);
323         }
324     } else {
325         supportedQualitiesList = { PrintQualityCode::PRINT_QUALITY_NORMAL };
326     };
327 
328     printerCaps.SetSupportedQuality(std::vector<uint32_t>(supportedQualitiesList.begin(),
329         supportedQualitiesList.end()));
330 
331     // compatible with Spooler USB
332     Json::Value supportedQualities;
333     for (auto &quality : supportedQualitiesList) {
334         Json::Value jsonObject;
335         uint32_t value = static_cast<uint32_t>(quality);
336         jsonObject["quality"] = value;
337         supportedQualities.append(jsonObject);
338     }
339     std::string attrString = PrintJsonUtil::WriteString(supportedQualities);
340     std::string keyword = "print-quality-supported";
341     printerCaps.SetPrinterAttrNameAndValue(keyword.c_str(), attrString.c_str());
342 }
343 
SetOptionAttributeFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)344 void SetOptionAttributeFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
345 {
346     Json::Value options;
347     if (ppd->nickname) {
348         options["make"] = ppd->nickname;
349     } else if (ppd->modelname) {
350         options["make"] = ppd->modelname;
351     } else {
352         options["make"] = "Bad PPD File";
353     }
354 
355     Json::Value cupsOptionsJson = printerCaps.GetPrinterAttrGroupJson();
356     options["cupsOptions"] = cupsOptionsJson;
357 
358     std::string optionStr = PrintJsonUtil::WriteString(options);
359     PRINT_HILOGD("SetOption: %{public}s", optionStr.c_str());
360     printerCaps.SetOption(optionStr);
361 }
362 
ParsePrinterAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)363 void ParsePrinterAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
364 {
365     ParseColorModeAttributesFromPPD(ppd, printerCaps);
366     ParseDuplexModeAttributesFromPPD(ppd, printerCaps);
367     ParsePageSizeAttributesFromPPD(ppd, printerCaps);
368     ParseQualityAttributesFromPPD(ppd, printerCaps);
369     ParseMediaTypeAttributeFromPPD(ppd, printerCaps);
370     SetOptionAttributeFromPPD(ppd, printerCaps);
371 }
372 
373 }